Tomcat server.xml配置详细解释

Tomcat 是一个 HTTP server。同时,它也是一个 serverlet 容器,可以执行 java 的 Servlet,也可以把 JavaServer Pages(JSP)和 JavaServerFaces(JSF)编译成 Java Servlet。它的模型图如下: 给个生产环境 server.xml 的例子: 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 <?xml version='1.0' encoding='utf-8'?> <Server port="-1" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <GlobalNamingResources> <Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" /> </GlobalNamingResources> <Service name="Catalina"> <Connector port="8080" server="www.rendoumi.com" protocol="org.apache.coyote.http11.Http11NioProtocol" maxHttpHeaderSize="8192" acceptCount="500" maxThreads="1000" minSpareThreads="200" enableLookups="false" redirectPort="8443" connectionTimeout="20000" relaxedQueryChars="[]|{}@!$*()+'.,;^\`&quot;&lt;&gt;" disableUploadTimeout="true" allowTrace="false" URIEncoding="UTF-8" useBodyEncodingForURI="true" /> <Engine name="Catalina" defaultHost="localhost"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="false"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="access_log." suffix=".txt" fileDateFormat="yyyy-MM-dd" pattern="%a|%A|%T|%{X-Forwarded-For}i|%l|%u|%t|%r|%s|%b|%{Referer}i|%{User-Agent}i " resolveHosts="false"/> <Context path="" docBase="/export/servers/tomcat/webapps/web" /> </Host> </Engine> </Service> </Server> 分解开来一部分一部分的看: ...

2021年11月15日 · 3 分钟 · 444 字 · 八戒

GlusterFS文件系统的优化

公司用了6年的GlusterFS终于到了要调整卷参数的地步了。 小文件已经多到要影响 IO 的地步了。 首先说一下结论,GlusterFS 安装完成后,基本不需要调整任何参数。生产系统千万不可盲目! 然后我们这是有特殊情况,所以调优步骤如下(以卷名为 esign-vol 为例): #必须关掉NFS gluster volume set esign-vol nfs.disable on #必须保留10%的空间,避免塞爆卷空间 gluster volume set esign-vol cluster.min-free-disk 10% #本机有256G的内存,所以设置25G的读缓存 gluster volume set esign-vol performance.cache-size 25GB #读缓存中,单个文件的缓存,最大文件size是128MB,大于128MB的单个文件不缓存 gluster volume set esign-vol performance.cache-max-file-size 128MB #设置每个客户端都允许多线程,缺省是2,多个小文件增加为4 gluster volume set esign-vol client.event-threads 4 #设置服务器端对特定的卷允许多线程,缺省是1,多个小文件增加为4 gluster volume set esign-vol server.event-threads 4 #分割线,以下参数不要调整,除非明确知道后果 #设置 io 线程数量,这个值缺省是16,已经很大了,足够用 gluster volume set esign-vol performance.io-thread-count 16 #设置写缓冲区,这个值缺省是1M,弄大了如果停电什么的,会丢数据 gluster volume set esign-vol performance.write-behind-window-size: 1M 以上就可以了。还有个参数 global-threading ,缺省是 off,不要设置为 on,有使用条件的,弄错了反而会导致性能降低。

2021年11月15日 · 1 分钟 · 73 字 · 八戒

Python的协程详细解释

在实际中遇到这样一个问题,公司软件发布上线自动化。 说简单点,就是需要去登录一个上线的内部网站,然后爬下所有的上线数据。 然后根据爬下来的数据整理好,可以一起上线的,就并发多线程,其实就是去传参数点击一个链接等返回。 不能并发的就单线程点链接。 那这个事情必须更有效率,单线程的没问题,用 python 的 request 就可以实现了。 我们仔细研究一下协程,先讲一下历史: 使用Python的人往往纠结在多线程、多进程,哪个效率更高?到底用哪个好呢? 其实 Python 的多进程和多线程,相对于别家的协程和异步处理机制,都不行,线程之间切换耗费 CPU 和寄存器,OS 的调度不可控,多进程之间通讯也不便。性能根本不行。 后来呢 Python 改进了语法,出现了 yiled from 充当协程调度,有人就根据这个特性开发了第三方的协程框架,Tornado,Gevent等。 官方也不能坐视不理啊,任凭别人出风头,于是 Python 之父深入简出3年,苦心钻研自家的协程,async/await 和 asyncio 库,并放到 Python3.5 后成为官方原生的协程。 对于 http请求、读写文件、读写数据库这种高延时的 IO 操作,协程是个大杀器,优点非常多;它可以在预料到一个阻塞将发生时,挂起当前协程,跑去执行其它协程,同时把事件注册到循环中,实现了多协程并发,其实这玩意是跟 Nodejs 的回调学的。 看下图,详细解释下,左边我们有100个网页请求,并发100个协程请求(其实也是1个1个发),当需要等待长时间回应回应时,挂起当前协程,并注册一个回调函数到事件循环(Event Loop)中,执行下一个协程,当有协程事件完成再通过回调函数唤醒挂起的协程,然后返回结果。 这个跟 nodejs 的回调函数基本一样,我们必须注意主进程和协程的关系,如果我在一个主进程中,触发协程函数,有100个协程,那么必须等待100个协程都结束后,才能回到正常的那个主进程中。当然,主进程也可能也是一个协程。 那么协程的基本用法 async f(n) 声明一个函数是协程的 await f(n) 挂起当前协程,把控制权交回 event loop,并且执行f(n)和注册之后的f(n)回调。 举个例子:如果在 g() 这个函数中执行了 await f(),那么g()函数会被挂起,并等待 f() 函数有结果结束,然后返回 g() 继续执行。 async def get(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() 最后一行 await 是挂起命令,挂起当前函数 get() ,并执行 response.text() 和注册回调,等待 response.text() 执行完成后重新激活当前函數get()继续执行,返回。 ...

2021年11月12日 · 2 分钟 · 402 字 · 八戒

kubernetes使用filebeat multiline自定义收集日志

我们介绍了如何在 kubernetes 环境中使用 filebeat sidecar 方式收集日志 使用的是 filebeat 的 moudle 模块,但凡是常用的软件,基本都有对应的模块可用,所以我们首先应该使用模块来收集日志。 那对于一些我们自己写的 Go 软件呢,或者根本不是标准的,那该怎么办呢? 那就自定义 filebeat.inputs,网上的 filebeat 例子,其实大多都是这样的 很简单,给个例子 [beat-logstash-some-name-832-2015.11.28] IndexNotFoundException[no such index] at org.elasticsearch.cluster.metadata.(IndexNameExpressionResolver.java:566) at org.elasticsearch.cluster.metadata.(IndexNameExpressionResolver.java:133) at org.elasticsearch.cluster.metadata.(IndexNameExpressionResolver.java:77) at org.elasticsearch.action.admin..checkBlock(TransportDeleteIndexAction.java:75) 看如上日志,如果不用模式匹配的话,那么会送5条记录到 ES, Kibana 看起来就十分割裂了。 所以要用正则把底下的4行和上面的第1行合在一起,合并成一条就记录推送到 ES 去 仔细观察,开头一行是以 [ 开始的,所以正则就是 ‘^\[’,我们要匹配的是底下的4行,必须反转一下模式。 multiline 的 negate 属性,这个是指定是否反转匹配到的内容,这里如果是 true 的话,反转,那就是选择不以 [ 开头的行。 netgate 属性的缺省值是 false multiline 的 match 属性,after 指追加到上一条事件(向上合并),before 指合并到下一条(向下合并) 于是,我们就可以写出以下的匹配模式,这样就把匹配到条目追加到上一条,合并在一起了: filebeat.inputs: - type: log enabled: true paths: - /Users/liuxg/data/multiline/multiline.log multiline.type: pattern multiline.pattern: '^\[' multiline.negate: true multiline.match: after output.elasticsearch: hosts: ["localhost:9200"] index: "multiline" 再给个例子,java 的 stack 调用,日志格式如下: ...

2021年11月11日 · 2 分钟 · 231 字 · 八戒

生产环境kubernetes使用持久化卷GlusterFS

在生产环境使用 Kubernetes ,绕不过去的一个问题就是持久化卷。 如果是使用阿里 ACK 托管平台的话,可以用 OSS 来持久化卷,如果是自搭的 kubernetes,那么存储就需要仔细考虑了。 ceph比较复杂,容易出故障。nfs 也不可用,毛病多多。minio倒是可以。 这种情况下使用双副本的 GlusterFS 就是不错的选择。 生产环境就不能随意了,最好不要使用 Heketi,因为凡是要持久化的东西,都是比较重要的东西,最好都有 yaml 记录。 GlusterFS 的搭建就不说了。说说实际使用过程: 一、装GFS,生产新卷 安装就不说了,我们的GFS有两个节点,172.19.20.18 和 172.19.20.36,我们强制建立一个两副本的卷: kuaijian-vol gluster volume create kuaijian-vol replica 2 transport tcp 172.19.20.18:/glusterfs/kuaijian-vol 172.19.20.36:/glusterfs/kuaijian-vol force 二、为k8s产生GFS的endingpoint和service cat << EOF >> ep-svc.yaml --- apiVersion: v1 kind: Service metadata: name: gfs-cluster_svc spec: ports: - port: 1 --- apiVersion: v1 kind: Endpoints metadata: name: gfs-cluster_svc subsets: - addresses: - ip: 172.19.20.18 ports: - port: 1 - addresses: - ip: 172.19.20.36 ports: - port: 1 EOF kubectl apply -f ep-svc.yaml 这里要提一个概念,通常情况下 service 是通过 selector 标签来选择对应的 pod 来增加 endingpoint 的。如下: ...

2021年11月10日 · 2 分钟 · 272 字 · 八戒

kubernetes生产环境使用filebeat sidecar收集日志

在生产环境中,ES 通常是不会在 k8s 集群中存在的,一般 MySQL 和 Elasticsearch 都是独立在 k8s 之外。 那么无论哪种 pod,要甩日志到 ES,最轻量的方案肯定是用 filebeat 甩过去了。 当然,如果是阿里的 ACK,logtail 和 logstore 配搭已经非常不错了,根本用不到 filebeat 和 ES。 可但是,我们不想为阿里 sls、logstore 出钱买单,就只能用 filebeat + ES 了 说一下 filebeat 的 sidecar 边车(僚机)用法: 如上图所示,简单说就是起一个 filebeat 的 logging-agent 边车(僚机),边车和主应用之间共享某个文件夹(mountPath),达到收集主应用日志并发送到 ES,而不用动 app-container 分毫。 我们以部署一个 Tomcat 应用为例来说明: 一、打造 filebeat 边车镜像 首先准备 Dockerfile FROM alpine:3.12 ARG VERSION=7.15.1 COPY docker-entrypoint.sh / RUN set -x \ && cd /tmp \ && wget https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${VERSION}-linux-x86_64.tar.gz \ && tar xzvf filebeat-${VERSION}-linux-x86_64.tar.gz \ && mv filebeat-${VERSION}-linux-x86_64 /opt \ && rm /tmp/* \ && chmod +x /docker-entrypoint.sh ENV PATH $PATH:/opt/filebeat-${VERSION}-linux-x86_64 WORKDIR /opt/filebeat-${VERSION}-linux-x86_64 ENTRYPOINT ["/docker-entrypoint.sh"] 我们以 alphine:3.12 为底版,然后下载 filebeat 7.15.1的二进制包并释放到 /opt 下,最后指定入口文件 /docker-entrypoint.sh ...

2021年11月10日 · 2 分钟 · 369 字 · 八戒

haproxy一个端口跑多个服务

我们选择 haproxy 1.8 版本以上的,编译安装到路径 /export/servers/haproxy make TARGET=linux2628 PREFIX=/export/servers/haproxy USE_GETADDRINFO=1 USE_ZLIB=1 USE_REGPARM=1 USE_OPENSSL=1 \ USE_SYSTEMD=1 USE_PCRE=1 USE_PCRE_JIT=1 USE_NS=1 make install PREFIX=/export/servers/haproxy 编辑 haproxy.conf 配置文件: global maxconn 5120 chroot /export/servers/haproxy daemon quiet nbproc 2 pidfile /tmp/haproxy.pid defaults timeout connect 5s timeout client 50s timeout server 20s listen http bind :80 timeout client 1h tcp-request inspect-delay 2s acl is_http req_proto_http tcp-request content accept if is_http server server-http :8080 use_backend ssh if !is_http backend ssh mode tcp timeout server 1h server server-ssh :22 解释一下:我们在 8080 端口开了 http 服务,在 22 端口开了 ssh 服务,80端口由 haproxy 做代理转发,首先判断客户端请求是否是 http 请求,如果是就转发到 8080 端口,如果不是,就转发到 22 端口,这样就实现了 80 端口同时跑 http 和 ssh 两个服务。 ...

2021年11月10日 · 1 分钟 · 102 字 · 八戒

sslh的一个端口同时跑多个服务

Ucloud的机器在两会期间干脆端口全灭,firewall设置进来的端口全关闭!!!!!! 还好有个Global ssh的服务,可以 ssh ubuntu@111.129.37.89.ipssh.net 登录上去,注意,直接ssh ubuntu@111.129.37.89是不通的。 那我们就搭建一个SSLH服务,可以把ssh和openvpn以及ssl服务统统塞到一个端口22里 动手吧 1、修改openssh的端口 从22端口改成2222,千万别重启,22这会先得归ssh用 2、安装sslh sudo apt install sslh vi /etc/default/sslh 找到Run=no 改成Run=yes 然后到下面,按需配置(不跑443的话可以不配) DAEMON_OPTS="--user sslh --listen 0.0.0.0:22 --ssh 127.0.0.1:2222 --ssl 127.0.0.1:443 --openvpn 127.0.0.1:1194 --pidfile /var/run/sslh/sslh.pid --timeout 5" 3、配置sslh并且重启服务器 sudo systemctl enable sslh sudo reboot 就搞定了

2021年11月10日 · 1 分钟 · 43 字 · 八戒

Nginx的一个端口同时跑SSH和HTTPS服务

这个要求挺古怪的,背景是防火墙只开了 nginx 443 端口。我也想同时 ssh 登录进去,但是F5没开IP 就只能这么干了,让 Nginx 一个端口跑多个服务 在 nginx.conf 加一段,stream 配置,nginx 的 ip 是 192.168.8.110: #Multi Ports stream { upstream ssh { server 192.168.8.112:22; } upstream https { server 192.168.8.111:443; } map $ssl_preread_protocol $upstream { default ssh; "TLSv1.2" https; "TLSv1.3" https; "TLSv1.1" https; "TLSv1.0" https; } # SSH and SSL on the same port server { listen 443; proxy_pass $upstream; ssl_preread on; } } 测试一下: ...

2021年11月09日 · 1 分钟 · 75 字 · 八戒

Kubernetes创建普通账号

kubernetes 装好正常运行一段时间后,会出现要把研发和运维权限分开的场景: 比如: 给某个用户某一指定名称空间下的管理权限 给用户赋予集群的只读权限 … 非常麻烦,我们这里不讨论过多的概念,从运维的角度出发,简单实用化 我们需要明确三个RBAC最基本的概念 Role: 角色,它定义了一组规则,定义了一组对Kubernetes API对象的操作权限 RoleBinding: 定义了"被作用者"和"角色"的绑定关系 Subject: 被作用者,既可以是"人",也可以是机器,当然也可以是 Kubernetes 中定义的用户(ServiceAccount主要负责kubernetes内置用户) 我们的操作过程流程如下,首先创建客户端证书;其次创建Role角色;再创建RoleBinding,把Subject和Role绑定,就完事了;最后一步是生成 kubectl 的配置文件。 一、创建客户端证书 我们以已建好的阿里 ACK 为例,或者自建好的 Kubernetes 也行;确定已经有了 .kube/config 配置文件,拥有集群最高权限,并且可以正常执行 kubectl 命令。 首先是生成证书,并向集群提出证书请求并签发,脚本如下: #!/bin/sh useraccount=reader openssl req -new -newkey rsa:4096 -nodes -keyout $useraccount-k8s.key -out $useraccount-k8s.csr -subj "/CN=$useraccount/O=devops" csr=$(cat $useraccount-k8s.csr | base64 | tr -d '\n') cat << EOF > k8s-csr.yaml apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: $useraccount-k8s-access spec: groups: - system:authenticated request: $csr usages: - client auth EOF kubectl create -f k8s-csr.yaml kubectl certificate approve $useraccount-k8s-access kubectl get csr $useraccount-k8s-access -o jsonpath='{.status.certificate}' | base64 --decode > $useraccount-k8s-access.crt kubectl config view -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' --raw | base64 --decode - > k8s-ca.crt 解释一下:我们定义了一个用户CN=reader,然后向集群发送了证书请求并签发,最终从集群获得了 reader-k8s-access.crt 的客户端证书和 k8s-ca.crt 的 CA 证书。 ...

2021年11月09日 · 3 分钟 · 427 字 · 八戒