当前位置: 首页 > news >正文

Tomcat 集群介绍

一.Tomcat 集群介绍

在实际生产环境中,单台 Tomcat 服务器的负载能力或者说并发能力在四五百左右。大
部分情况下随着业务增长,访问量的增加(并发量不止四五百),单台 Tomcat 服务器是
无法承受的。这时就需要将多台 Tomcat 服务器组织起来,共同分担负载。

所以在生产环境中,一部分会使用单机部署方式,这样的部署方式在比较小的公司会见
到,大部分会使用多机部署。具体的 Tomcat 部署架构有以下几种:

  1. 单台 Tomcat 服务器部署时称为单机部署
    Tomcat 单独运行,直接接受用户的请求

  2. 单台 Tomcat 服务器加上 Nginx 或者 Httpd 作为反向代理
    反向代理,单机运行,提供了一个 Nginx 作为反向代理,可以做到静态由 nginx
    提供响应,动态 jsp 代理给 Tomcat,如 LNMT 或者 LAMT 架构

    • LNMT:Linux + Nginx + MySQL + Tomcat
    • LAMT:Linux + Apache(Httpd)+ MySQL + Tomcat
  3. 使用 Nginx 反向代理多台 Tomcat 服务器
    前置一台 Nginx,给多台 Tomcat 实例做反向代理和负载均衡调度,Tomcat 上
    部署的纯动态页面更适合

    • LNMT:Linux + Nginx + MySQL + Tomcat
  4. 使用 Nginx 反向代理多台 Tomcat 服务器,在每台 Tomcat 服务器又使用 Nginx
    接收请求,如 LNNMT

    • LNNMT:Linux + Nginx + Nginx + MySQL + Tomcat

二.负载均衡策略

  1. 轮询
    加权轮询是 upstrean 模块默认的负载均衡策略。每个请求会按时间顺序逐一
    分配到不同的后端服务器。默认情况下每台服务器的权重为 1,若服务器硬件
    性能差别比较大,则需要为不同的服务器分配不同的权重。
upstream serverpool{server localhost:8000;  # server指令指明后端服务器server localhost:9000;server www.suosuoli.cn weight=1 fail_timeout=5s max_fail=3;
}server {listen 88;server_name localhost;location / {proxy_pass http://serverpool/;}
}

upstream 模块中的 server 指令参数说明:

参数描述
fail_timeout与 max_fails 结合使用
max_fails设置在 fail_timeout 参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被访为是停机了
fail_time服务器会被认为停机的时间长度,默认为 10s
backup标记该服务器为备用服务器。当主服务器停止时,请求会被发送到它这里
down标记服务器永久停机了
  1. ip_hash
    指定负载均衡器按照基于客户端 IP 的分配方式,这个方法确保了相同的客户端的
    请求一直发送到相同的服务器,以保证客服端和服务器的 session 会话。这样每
    个访客都固定访问一个后端服务器,可以解决 session 不能跨服务器的问题。
upstream serverpool{1p_hash;server 192.168.192.122:8080 weight=3;server 192.168.192.133:8080 weight=1;
}

三.Tomcat 的 session 共享

当单机的 Tomcat,演化出多机多级部署的时候,一个问题便凸显出来,这就是
Session。而这个问题的由来,是由于 HTTP 协议在设计之初没有想到未来的发展。

HTTP 的无状态、有连接和短连接特性

  • 无状态:指的是服务器端无法知道 2 次请求之间的联系,即使是前后 2 次请求
    来自同一个浏览器,也没有任何数据能够判断出是同一个浏览器的请求。后来可
    以通过 cookie、session 机制来判断。
    • 浏览器端第一次通过 HTTP 请求服务器端时,在服务器端使用 session 技术,
      就可以在服务器端产生一个随机值即 SessionID 发给浏览器端,浏览器端收到
      后会保持这个 SessionID 在 Cookie 当中,这个 Cookie 值一般不能持久存
      储,浏览器关闭就消失。浏览器在每一次提交 HTTP 请求的时候会把这个
      SessionID 传给服务器端,服务器端就可以通过比对知道当前是谁访问。
    • Session 通常会保存在服务器端内存中,如果没有持久化,则易丢失
    • Session 会定时过期。过期后浏览器如果再访问,服务端发现没有此 ID,将给
      浏览器端重新发新的 SessionID
    • 更换浏览器也将重新获得新的 SessionID
  • 有连接:是因为 HTTP1.x 基于 TCP 协议,是面向连接的,需要 3 次握手、4 次
    挥手断开。
  • 短连接:Http 1.1 之前,都是一个请求一个连接,而 Tcp 的连接创建销毁成本高
    ,对服务器有很大的影响。所以,自 Http 1.1 开始,支持 keep-alive,默认也开
    启,一个连接打开后,会保持一段时间(可设置),浏览器再访问该服务器就使用这
    个 Tcp 连接,减轻了服务器压力,提高了效率。

如果应用需要用户进行登录,Nginx 作为反向代理服务器代理后端 Tomcat 接收请求,
那么这个时候,就会出现 Session 相关的问题。

解决这种由于反向代理服务而导致的使得同一个客户端的请求找不到自己对应的
Session 的问题可以使用 Session 共享机制。有以下几种方案:

3.1 ip_hash 策略

由一个用户发起的请求,只会被调度到特定的 TomcatA 服务器上进行处理,另一个
用户发起的请求只在 TomcatB 上进行处理。那么这个时候,同一个用户发起的请
求,都会通过 nginx 的 ip_hash 策略,将请求转发到其中的一台 Tomcat 上。

在 Nginx 的反向代理中 ip_hash 策略也叫 source ip,即源地址哈希;如果使用
HAProxy 做代理服务器,则可以使用 Cookie 来保持会话。

3.2 Session 复制集群

Session 复制的原理是通过 Tomcat 集群的内部多播机制将所有的不同 Session 在
集群的所有 Tomcat 主机上复制,即所有 Tomcat 服务器都存有当前的所有 Session
信息。

缺点

  • Tomcat 的同步节点不宜过多,互相即时通信同步 session 需要太多带宽
  • 每一台都拥有全部 session,内存占用太多

3.3 Session Server

session 共享服务器,共享的 Session 服务器使用 memcached 或者 redis 做存
储,来存储 session 信息,供 Tomcat 服务器查询。

3.4 简单的 Nginx 调度和 Session 共享示例

3.4.1 示例使用环境

环境规划

IP主机名服务备注
192.168.142.151t0调度器Nginx、HTTPD
192.168.142.152t1tomcat1JDK8、Tomcat8
192.168.142.153t2tomcat2JDK8、Tomcat8

每台主机使用 hosts 文件解析 ip

192.168.142.151 t0.suosuoli.cn t0
192.168.142.152 t1.suosuoli.cn t1
192.168.142.153 t2.suosuoli.cn t2

3.4.2 Tomcat 配置

先编写测试使用的 jsp:位于 t1 和 t2 节点的/data/webapps/index.jsp

<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>lbjsptest</title>
</head>
<body>
<div>On  <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html

t1 虚拟主机配置

<Engine name="Catalina" defaultHost="t1.suosuoli.cn"><Host name="t1.suosuoli.cn" appBase="/data/webapps" autoDeploy="true" />
</Engine>

t2 虚拟主机配置

<Engine name="Catalina" defaultHost="t2.suosuoli.cn"><Host name="t2.suosuoli.cn" appBase="/data/webapps" autoDeploy="true" />
</Engine>

每台 Tomcat 配置大同小异:

# 环境变量配置
vim /etc/profile.d/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$CATALINA_HOME/bin:$PATH# 项目路径配置
mkdir -pv /data/webapps/ROOT# 编写测试jsp文件,内容在上面
vim /data/webapps/index.jsp
scp -r server.xml 192.168.142.153:/usr/local/tomcat# 启动Tomcat服务startup.sh

3.4.3 Nginx 配置

upstream backendpool {#ip_hash; # 先禁用观察轮询的sessionid变化,之后开启开session黏性server t1.suosuoli.cn:8080;server t2.suosuoli.cn:8080;}server {location ~* \.(jsp|do)$ {proxy_pass http://backendpool;}}

访问测试http://t0.suosuoli.cn/index.jsp,可以看到轮询调度效果。
在 upstream 中使用 ip_hash 指令,使用客户端 IP 地址 Hash。这个 hash
值使用 IP v4 地址的前 24 位或全部的 IP v6 地址。
配置完 reload nginx 服务。测试一下观察效果。关闭 Session 对应的 Tomcat
服务,再重启 tomcat,观察 Session 的变化。

3.5 简单的 Httpd 调度

使用httpd -M命令可以看到 proxy_balancer_module模块,httpd 用它来实现
负载均衡。在 Tomcat 和 Httpd 集成时,可以使用两种协议来实现请求的负载均衡:

方式依赖模块
http 负载均衡mod_proxy mod_proxy_http mod_proxy_balancer
ajp 负载均衡mod_proxy mod_proxy_ajp mod_proxy_balancer

3.5.1 Httpd 配置说明

# 关闭httpd默认主机
~$ cd /etc/httpd/conf
~$ vim httpd.conf
# 注释掉 DocumentRoot "/var/www/html"
~$ cd ../conf.d
~$ vim vhosts.conf  # 编辑虚拟主机...# 配置代理到balancerProxyPass [path] !|url [key=value [key=value ...]]# Balancer成员BalancerMember [balancerurl] url [key=value [key=value ...]]# 设置Balancer或参数ProxySet url key=value [key=value ...]...
~$ httpd -t
~$ systemctl start httpd

ProxyPassBalancerMember指令参数说明

参数缺省值说明
min0连接池最小容量
max1 ~ n连接池最大容量
retry60apache 请求发送到后端服务器错误后等待的时间秒数。0 表示立即重试

Balancer 参数说明

参数缺省值说明
loadfactor-定义负载均衡后端服务器权重,取值范围 1 - 100
lbmethodbyrequests负载均衡调度方法。 byrequests 基于权重的统计请求个数进行调度; bytrafficz 执行基于权重的流量计数调度; bybusyness 通过考量每个后端服务器当前负载进行调度
maxattempts1放弃请求前实现故障转移的次数,默认为 1,其最大值不应该大于总的节点数
nofailoverOff如果后端服务器没有 Session 副本,可以设置为 On 不允许故障转移。Off 故障可以转移
stickysession-调度器的 sticky session 名字,根据 web 后台编程语言不同,可以设置为 JSESSIONID 或 PHPSESSIONID

ProxySet 指令也可以使用上面的参数。如下面的配置示例:

<Proxy "balancer://hotcluster">BalancerMember "http://www2.example.com:8080" loadfactor=1BalancerMember "http://www3.example.com:8080" loadfactor=2ProxySet lbmethod=bytraffic
</Proxy>
<Proxy "http://backend">ProxySet keepalive=On
</Proxy>
ProxySet "balancer://foo" lbmethod=bytraffic timeout=15
ProxySet "ajp://backend:7001" timeout=15

conf.d/vhosts.conf内容如下

<VirtualHost *:80>ProxyRequests     OffProxyVia          OnProxyPreserveHost OnProxyPass        / balancer://lbtomcats/ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
<Proxy balancer://lbtomcats>BalancerMember http://t1.suosuoli.cn:8080 loadfactor=1BalancerMember http://t2.suosuoli.cn:8080 loadfactor=2
</Proxy>

loadfactor 设置为 1:2,便于观察。观察调度的结果是轮询的。

``使用 session 粘性 - 修改conf.d/vhosts.conf**

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/"
env=BALANCER_ROUTE_CHANGED
<VirtualHost *:80>ProxyRequests     OffProxyVia          OnProxyPreserveHost OnProxyPass        / balancer://lbtomcats/ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
<Proxy balancer://lbtomcats>BalancerMember http://t1.suosuoli.cn:8080 loadfactor=1 route=Tomcat1BalancerMember http://t2.suosuoli.cn:8080 loadfactor=2 route=Tomcat2ProxySet stickysession=ROUTEID
</Proxy>

观察会发现 Session 不变,一直找的同一个 Tomcat 服务器。

ajp 调度 - 修改conf.d/vhosts.conf

<VirtualHost *:80>ProxyRequests     OffProxyVia          OnProxyPreserveHost OnProxyPass        / balancer://lbtomcats/ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
<Proxy balancer://lbtomcats>BalancerMember ajp://t1.suosuoli.cn:8009 loadfactor=1 route=Tomcat1BalancerMember ajp://t2.suosuoli.cn:8009 loadfactor=2 route=Tomcat2#ProxySet stickysession=ROUTEID
</Proxy>

ProxySet stickysession=ROUTEID 先禁用看看切换效果,开启后看看黏住效果。
开启后,发现 Session 不变了,一直找的同一个 Tomcat 服务器。

虽然,上面的做法实现客户端在一段时间内找同一台 Tomcat,从而避免切换后导致
的 Session 丢失。但是如果 Tomcat 节点挂掉,那么 Session 依旧丢失。

假设有 A、B 两个节点,都把 Session 做了持久化。如果 Tomcat A 服务下线期间
用户切换到了 Tomcat B 上,就获得了 Tomcat B 的 Session,就算持久化
Session 的 Tomcat A 上线了,也没用了。

3.5.2 Tomcat 配置说明

在 tomcat 的配置中 Engine 使用 jvmRoute 属性

t1、t2的tomcat配置中分别增加jvmRoute
<Engine name="Catalina" defaultHost="t1.suosuoli.cn" jvmRoute="Tomcat1">
<Engine name="Catalina" defaultHost="t2.suosuoli.cn" jvmRoute="Tomcat2">

这样在生成 session 时的 SessionID,就变成了类似这样
SessionID = 9C949FA4AFCBE9337F5F0669548BD4DF.Tomcat2

四.Tomcat 集群使用组播复制 session 示例

配置详细参考

配置示例:

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"channelSendOptions="8"><Manager className="org.apache.catalina.ha.session.DeltaManager"expireSessionsOnShutdown="false"notifyListenersOnReplication="true"/><Channel className="org.apache.catalina.tribes.group.GroupChannel"><Membership className="org.apache.catalina.tribes.membership.McastService"address="230.100.100.8"port="45564"frequency="500"dropTime="3000"/><Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"address="auto"port="4000"autoBind="100"selectorTimeout="5000"maxThreads="6"/><Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"><Transport
className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/></Sender><Interceptor
className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/><Interceptor
className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/></Channel><Valve className="org.apache.catalina.ha.tcp.ReplicationValve"filter=""/><Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/><Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"tempDir="/tmp/war-temp/"deployDir="/tmp/war-deploy/"watchDir="/tmp/war-listen/"watchEnabled="false"/><ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>

以上配置说明

  • Cluster 集群配置
  • Manager 会话管理器配置
  • Channel 信道配置
    • Membership 成员判定。使用什么多播地址、端口多少、间隔时长 ms、超时时
      长(ms)等。同一个多播地址和端口认为同属一个组。使用时修改这个多播地址,
      以防冲突
    • Receiver 接收器,多线程接收多个其他节点的心跳、会话信息。默认会从
      4000 到 4100 依次尝试可用端口。
      • address="auto",auto 可能绑定到 127.0.0.1 上,所以一定要改为可以
        用的 IP
    • Sender 多线程发送器,内部使用了 tcp 连接池。
    • Interceptor 拦截器
  • Valve
    • ReplicationValve 检测哪些请求需要检测 Session,Session 数据是否有了
      变化,需要启动复制过程
  • ClusterListener
    • ClusterSessionListener 集群 session 侦听器

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
添加到<Engine>的配置上下文,则所有虚拟主机都可以启用 Session 复制。添加
<Host>配置上下文,则表示该虚拟主机可以启用 Session 复制。最后,在应用
程序内部启用了才可以使用 session 复制功能。

该示例的前提是:

  • 时间同步,确保 NTP 或 Chrony 服务正常运行。# systemctl status chronyd
  • 关闭防火墙规则。# systemctl stop firewalld

本次把多播复制的配置放到缺省虚拟主机里面, 即 Host 之下。
特别注意修改 Receiver 的 address 属性为一个本机可对外的 IP 地址。

t1 的 server.xml 中,如下

<Host name="t1.magedu.com" appBase="/data/webapps" autoDeploy="true" ><!-- 其他略去 --><Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"address="192.168.142.152"port="4000"autoBind="100"selectorTimeout="5000"maxThreads="6"/>

t2 的 server.xml 中,如下

<Host name="t2.magedu.com" appBase="/data/webapps" autoDeploy="true" ><!-- 其他略去 --><Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"address="192.168.142.153"port="4000"autoBind="100"selectorTimeout="5000"maxThreads="6"/>

Tomcat 重启后,ss 命令能看到 tomcat 监听在 4000 端口上

有了以上的准备工作后还要配置web.xml
在应用中增加 WEB-INF,从全局复制一个 web.xml 过来
# cp /usr/local/tomcat/conf/web.xml /data/webapps/ROOT/WEB-INF/
为 web.xml 的<web-app>标签增加子标签<distributable/>来开启该应用程序
的分布式。
重启全部 Tomcat,通过负载均衡调度到不同节点,返回的 SessionID 不变了。

上面的示例为简单的 Session 复制共享,使用 Tomcat 内置的集群和广播机制来实现,
这种做法只能在小型应用中使用,少数 Tomcat 应用。如果 Tomcat 的服务器数量过
多,比如有 N 台服务器,那么某用户登录一次,则每台 Tomcat 服务器需要向集群中
的另外N-1台服务器复制该 Session,那么互相之间大量的复制 Session 将会占用
大量带宽。官方文档支出,超出 4 个 Tomcat 服务器就不适合使用该方案了。

五.Tomcat 集群使用 session 服务器共享 session 示例

5.1 NoSQL 概念

NoSQL 是对非 SQL、非传统关系型数据库的统称。
NoSQL 一词诞生于 1998 年,2009 年这个词汇被再次提出指非关系型、分布式、
不提供 ACID 的数据库设计模式。

随着互联网时代的到来,数据爆发式增长,数据库技术发展日新月异,要适应新的
业务需求。随着移动互联网、物联网的到来,大数据技术中 NoSQL 也同样重要。

从数据库的排行可以看出 NoSQL 数据库的重要性:db-engines.com

NoSQL 数据库分类

存储类型代表数据库产品
Key-value Storeredis、memcached
Document Storemongodb、CouchDB
Column Store 列存数据库(Column-Oriented DB)HBase、Cassandra
Graph DBNeo4j
Time Series 时序数据库InfluxDB

5.2 Memcached

Memcached 只支持能序列化的数据类型,不支持持久化,基于 Key-Value 存储的
内存缓存系统。

5.2.1 Memcached 内存分配机制

应用程序运行需要使用内存来存储数据,但对于一个缓存系统来说,申请内存、释放
内存将十分频繁,非常容易导致大量内存碎片,最后导致无连续可用内存可用。

Memcached 采用了 Slab Allocator 机制来分配、管理内存。

Page:分配给 Slab 的内存空间,默认为 1MB,分配后就得到一个 Slab。Slab 分配
之后内存按照固定字节大小等分成 chunk。
Chunk:用于缓存记录 kv 值的内存空间。Memcached 会根据数据大小选择存到哪一
个 chunk 中,假设 chunk 有 128bytes、64bytes,数据只有 100bytes 存储在
128bytes 的 chunk 中,存在些浪费。
Chunk 最大就是 Page 的大小,即一个 Page 中就一个 Chunk
Slab Class:Slab 按照大小分组,就组成不同的 Slab Class

如果有 100bytes 要存,那么 Memcached 会选择上图中 Slab Class 2 存储,因为
它是 120bytes 的 Chunk。
Slab 之间的差异可以使用 Growth Factor 参数控制,默认 1.25。

懒过期Lazy Expiration
memcached 不会监视数据是否过期,而是在取数据时才看是否过期,过期的把数据
有效期限标识为 0,并不清除该数据。以后可以覆盖该位置存储其它数据。

当内存不足时,memcached 会使用 LRU(Least Recently Used)机制来查找可用空间,分配给新纪录使用。

5.2.2 Memcached 集群

Memcached 集群,称为基于客户端的分布式集群。
Memcached 集群内部并不互相通信,一切都需要客户端连接到 Memcached 服务器
后自行组织这些节点,并决定数据存储的节点。

安装 memcached

# yum install memcached
# rpm -ql memcached
/etc/sysconfig/memcached
/usr/bin/memcached
/usr/bin/memcached-tool
/usr/lib/systemd/system/memcached.service
/usr/share/doc/memcached-1.4.15
/usr/share/doc/memcached-1.4.15/AUTHORS
/usr/share/doc/memcached-1.4.15/CONTRIBUTORS
/usr/share/doc/memcached-1.4.15/COPYING
/usr/share/doc/memcached-1.4.15/ChangeLog
/usr/share/doc/memcached-1.4.15/NEWS
/usr/share/doc/memcached-1.4.15/README.md
/usr/share/doc/memcached-1.4.15/protocol.txt
/usr/share/doc/memcached-1.4.15/readme.txt
/usr/share/doc/memcached-1.4.15/threads.txt
/usr/share/man/man1/memcached-tool.1.gz
/usr/share/man/man1/memcached.1.gz
# cat /usr/lib/systemd/system/memcached.service
[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/memcached
ExecStart=/usr/bin/memcached -u $USER -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS
# cat /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""# 前台显示运行
# memcached -u memcached -p 11211 -f 1.25 -vv
# systemctl start memcached

修改 memcached 运行参数,可以使用下面的选项修改/etc/sysconfig/memcached
文件:

  • -u username memcached 运行的用户身份,必须普通用户
  • -p 绑定的端口,默认 11211
  • -m num 最大内存,单位 MB,默认 64MB
  • -c num 最大连接数,缺省 1024
  • -d 守护进程方式运行
  • -f 增长因子 Growth Factor,默认 1.25
  • -v 详细信息,-vv 能看到详细信息
  • -M 内存耗尽,不许 LRU
  • -U 设置 UDP 监听端口,0 表示禁用 UDP

与 memcached 通信的不同语言的连接器也需要下载,其中 libmemcached 提供
了 C 库和命令行工具。

# yum list all | grep memcached
memcached.x86_64                        1.4.15-10.el7_3.1              @base
libmemcached.i686                       1.0.16-5.el7                   base
libmemcached.x86_64                     1.0.16-5.el7                   base
libmemcached-devel.i686                 1.0.16-5.el7                   base
libmemcached-devel.x86_64               1.0.16-5.el7                   base
memcached-devel.i686                    1.4.15-10.el7_3.1              base
memcached-devel.x86_64                  1.4.15-10.el7_3.1              base
opensips-memcached.x86_64               1.10.5-4.el7                   epel
php-ZendFramework-Cache-Backend-Libmemcached.noarch
php-pecl-memcached.x86_64               2.2.0-1.el7                    epel
python-memcached.noarch                 1.48-4.el7                     base
uwsgi-router-memcached.x86_64           2.0.17.1-2.el7                 epel

5.2.3 访问 memcached 的协议

查看/usr/share/doc/memcached-1.4.15/protocol.txt可以看到 memcached 支持
的协议,其中 telnet 也支持:

# yum install telnet
# telnet locahost 11211
stats
add mykey 1 60 4
test
STORED
get mykey
VALUE mykey 1 4
test
END
set mykey 1 60 5
test1
STORED
get mykey
VALUE mykey 1 5
test1
END

add KEY FLAGS exptime bytes,该指令表示在 memcached 中增加键 KEY,FLAGS
为标识,过期时间为 exptime 秒,bytes 为存储数据的字节数。

5.3 使用 MSM 实现 tomcat 集群的 session 共享服务器

5.3.1 MSM

MSM(memcached session manager)提供将 Tomcat 的 session 保持到 memcached
或 redis 的功能的程序,可以实现高可用。目前项目托管在 Github-MSM
支持 Tomcat 的 6.x、7.x、8.x、9.x 等版本。

Tomcat 的 Session 管理类 jar 包,Tomcat 版本不同,使用不同的包

  • memcached-session-manager-2.3.2.jar
  • memcached-session-manager-tc8-2.3.2.jar

Session 数据的序列化、反序列化类

  • 官方推荐 kyro
  • 在 webapp 中 WEB-INF/lib/下

驱动类

  • memcached(spymemcached.jar)
  • Redis(jedis.jar)

5.3.2 MSM 在 Tomcat 的安装

MSM 安装官方参考

将 spymemcached.jar、memcached-session-manage、kyro 相关的 jar 文件都放
到 Tomcat 的 lib 目录中去,这个目录是$CATALINA_HOME/lib/,一般就是
/usr/local/tomcat/lib

asm-5.2.jar
kryo-3.0.3.jar
kryo-serializers-0.45.jar
memcached-session-manager-2.3.2.jar
memcached-session-manager-tc8-2.3.2.jar
minlog-1.3.1.jar
msm-kryo-serializer-2.3.2.jar
objenesis-2.6.jar
reflectasm-1.11.9.jar
spymemcached-2.12.3.jar

5.3.3 sticky 模式的 session 服务

原理

当请求结束时 Tomcat 的 session 会送给 memcached 备份。即 Tomcat session
为主 session,memcached session 为备 session,使用 memcached 相当于备份了
一份 Session。

查询 Session 时 Tomcat 会优先使用自己内存的 Session,Tomcat 通过
jvmRoute 发现不是自己的 Session,便从 memcached 中找到该 Session,更新本机
Session,请求完成后更新 memcached。

环境

<t1-tomcat服务器1>  <t2-tomcat服务器2>.        \ /       ..         X        ..        / \       .
<m1-tomcat服务器1>  <m2-tomcat服务器2>

t1 和 m1 部署在一台主机上,t2 和 m2 部署在同一台。

配置

配置放到 $CATALINA_HOME/conf/context.xml
特别注意,t1 配置中为 failoverNodes="nm1", t2 配置为
failoverNodes="m2"

以下是 sticky 的配置

<Context>...<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"memcachedNodes="m1:192.168.142.152:11211,m2:192.168.142.153:11211"failoverNodes="m1"requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/>
</Context>

关键配置memcachedNodes="n1:host1.yourdomain.com:11211,n2:host2.yourdomain.com:11211"

memcached 的节点组:m1、m2 只是别名,可以重新命名。failoverNodes 故障
转移节点,m1 是备用节点,m2 是主存储节点。另一台 Tomcat 将 m1 改为 m2,
其主节点是 m1,备用节点是 m2。

如果配置成功,启动 tomcat 后可以在logs/catalina.out中看到下面的内容

信息 [t1.magedu.com-startStop-1]
de.javakaffee.web.msm.MemcachedSessionService.startInternal --------
-  finished initialization:
- sticky: true
- operation timeout: 1000
- node ids: [m2]
- failover node ids: [m1]
- storage key prefix: null
- locking mode: null (expiration: 5s)

配置成功后,网页访问以下,页面中看到了 Session。然后运行下面的 Python 程序,
就可以看到是否存储到了 memcached 中了。

import memcache # pip install python-memcached
mc = memcache.Client(['192.168.142.152:11211', '192.168.142.153:11211'], debug=True)stats = mc.get_stats()[0]
print(stats)
for k,v in stats[1].items():print(k, v)print('-' * 30)
# 查看全部key
print(mc.get_stats('items')) # stats items 返回 items:5:number 1
print('-' * 30)for x in mc.get_stats('cachedump 5 0'):# stats cachedump 5 0 # 5和上面的items返回的值有
关;0表示全部print(x)

t1、t2、m1、m2 依次启动成功,分别使用http://t1.suosuoli.cn:8080/
http://t2.suosuoli.cn:8080/ 观察。

启动负载均衡调度器(Nginx),通过http://t0.suosuoli.cn来访问看看效果

On tomcats
192.168.142.153:8080
SessionID = 2A19B1EB6D9649C9FED3E7277FDFD470-n2.Tomcat1
Wed Jun 26 16:32:11 CST 2019
On tomcats
192.168.142.152:8080
SessionID = 2A19B1EB6D9649C9FED3E7277FDFD470-n1.Tomcat2
Wed Jun 26 16:32:36 CST 2019

可以看到浏览器端被调度到不同 Tomcat 上,但是都获得了同样的 SessionID。

停止 t2、n2 看看效果,恢复看看效果。

5.3.4 none-sticky 模式的 session 服务

原理
从 msm 1.4.0 之后开始支持 non-sticky 模式。

Tomcat session 为中转 Session,如果 m1 为主 session,m2 为备 session。
产生的新的 Session 会发送给主、备 memcached,并清除本地 Session。

m1 下线,m2 转正。m1 再次上线,m2 依然是主 Session 存储节点。

memcached 配置
配置放到 $CATALINA_HOME/conf/context.xml

<Context>...<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"memcachedNodes="m1:192.168.142.152:11211,m2:192.168.142.153:11211"sticky="false"sessionBackupAsync="false"lockingMode="uriPattern:/path1|/path2"requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/>
</Context>

redis 配置

下载 jedis.jar,放到$CATALINA_HOME/lib/,对应本次安装就是
/usr/local/tomcat/lib

# yum install redis
# vim /etc/redis.conf
bind 0.0.0.0
# systemctl start redis

以下配置放到 $CATALINA_HOME/conf/context.xml

<Context>...<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"memcachedNodes="redis://192.168.142.152:6379"sticky="false"sessionBackupAsync="false"lockingMode="uriPattern:/path1|/path2"requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"/>
</Context>

总结
通过多组实验,使用不同技术实现了 session 持久机制

  1. session 绑定,基于 IP 或 session cookie 的。其部署简单,尤其基于
    session 黏性的方式,粒度小,对负载均衡影响小。但一旦后端服务器有故障,
    其上的 session 丢失。
  2. session 复制集群,基于 tomcat 实现多个服务器内共享同步所有 session。
    此方法可以保证任意一台后端服务器故障,其余各服务器上还都存有全部 session,
    对业务无影响。但是它基于多播实现心跳,TCP 单播实现复制,当设备节点过多,
    这种复制机制不是很好的解决方案。且并发连接多的时候,单机上的所有 session
    占据的内存空间非常巨大,甚至耗尽内存。
  3. session 服务器,将所有的 session 存储到一个共享的内存空间中,使用多个
    冗余节点保存 session,这样做到 session 存储服务器的高可用,且占据业务服
    务器内存较小。是一种比较好的解决 session 持久的解决方案。

以上的方法都有其适用性。生产环境中,应根据实际需要合理选择。

不过以上这些方法都是在内存中实现了 session 的保持,可以使用数据库或者文件
系统,把 session 数据存储起来,持久化。这样服务器重启后,也可以重新恢复
session 数据。不过 session 数据是有时效性的,是否需要这样做,视情况而定。

相关文章:

Tomcat 集群介绍

一.Tomcat 集群介绍 在实际生产环境中&#xff0c;单台 Tomcat 服务器的负载能力或者说并发能力在四五百左右。大 部分情况下随着业务增长&#xff0c;访问量的增加(并发量不止四五百)&#xff0c;单台 Tomcat 服务器是 无法承受的。这时就需要将多台 Tomcat 服务器组织起来&a…...

Windows右键添加用 IDEA 打开

1.安装IDEA时 安装时会有个选项来添加&#xff0c;如下&#xff1a; 勾选即可 2.修改注册表 安装时未勾选&#xff0c;可以把下面代码中程序路径改为自己的&#xff0c;保存为对应的 idea.reg文件&#xff0c;双击即可 Windows Registry Editor Version 5.00[HKEY_CLASSES…...

Golang 中return和defer执行先后顺序

先给出最终结论&#xff1a; 执行return语句 -> 执行defer函数 -> 函数返回 这里可能会有一个疑问&#xff0c; 执行return语句和函数返回难道不是一回事? Golang语言中函数的return不是原子操作&#xff0c;而是分为了两步&#xff1a; 返回值赋值真正函数返回 Gol…...

业务数据模拟/采集

业务数据模拟/采集 2.2 业务数据模拟 2.2.1 连接MySQL 通过MySQL可视化客户端连接数据库。2.2.2 建表语句 1&#xff09;通过SQLyog创建数据库2&#xff09;设置数据库名称为gmall&#xff0c;编码为utf-8&#xff0c;排序规则为utf8_general_ci3&#xff09;导入数据库结构脚本…...

qt day 5

实现局域网的网络聊天室功能 1>服务器代码 --------------------------------------------------------------- widget.h --------------------------------------------------------------- #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMes…...

Java设计模式之适配器模式

适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式&#xff0c;它结合了两个独立接口的功能。 这种模式涉及到一个单一的类&#xff0c;该类负责加入独立的或不兼容的接口功能。举个真实的例子&#xff0…...

每天一个工业通信协议(3)2023.8.29 (DAP接口)

文章目录 参考文献1.DAP接口介绍2.DAP接口的2/3pin3.一种DAP接口方案应用的说明,通过两步初始化把JTAG接口变成DAP接口使用4.DAP接口的协议4.1 DAP电报的分类(只用JTAG类电报)4.2 电报格式4.3 DAP有限状态机参考文献 李婧. DAP模块验证组件系统级开发和实现[D]. 陕西:西安电…...

如何将Word转成PDF?试一下这个转换方法

Word转成PDF是现代办公中常见的需求&#xff0c;它可以确保文件的格式和内容在不同平台上保持一致&#xff0c;并且更加方便共享和打印。在这个数字化时代&#xff0c;我们经常需要将Word文档转换为PDF格式&#xff0c;无论是个人用户还是商务用户都会遇到这样的需求。那么如何…...

成都睿趣科技:现在开一家抖音小店还来得及吗

随着社交媒体的迅猛发展&#xff0c;抖音已经成为了一个全球范围内广受欢迎的社交平台。在这个短视频应用上&#xff0c;人们分享着各种各样的内容&#xff0c;从搞笑段子到美食教程&#xff0c;再到时尚搭配和手工艺品制作。随着用户数量的不断增长&#xff0c;很多人都在思考…...

原型链中:为什么Function.proto==Function.prototype?

背景: 在 JavaScript 中&#xff0c;每个函数&#xff08;包括构造函数&#xff09;都是一个对象&#xff0c;而对象都有一个 __proto__ 属性&#xff0c;指向它们的原型。当你创建一个函数时&#xff0c;JavaScript 引擎会自动为该函数创建一个原型对象&#xff0c;并将其关联…...

原生js实现轮播图及无缝滚动

我这里主要说轮播图和无缝滚动的实现思路&#xff0c;就采用最简单的轮播图了&#xff0c;当然实现的思路有很多种&#xff0c;我这也只是其中一种。 简单轮播图的大概结构是这样的&#xff0c;中间是图片&#xff0c;二边是箭头可以用来切换图片&#xff0c;下面的小圆点也可以…...

MP中的字段还可以利用函数来查询拼接sql

//根据value查询GetMapping("getTest")public List<HashMap> getTest() {QueryWrapper<TTest> queryWrapper new QueryWrapper<>();queryWrapper.eq("substr(name,1,2)","99999");List<TTest> list1 testService.list…...

【python爬虫】中央气象局预报—静态网页图像爬取练习

静态网页爬取练习 中央气象局预报简介前期准备步骤Python爬取每日预报结果—以降水为例 中央气象局预报简介 中央气象台是中国气象局&#xff08;中央气象台&#xff09;发布的七天降水预报页面。这个页面提供了未来一周内各地区的降水预报情况&#xff0c;帮助人们了解即将到来…...

数字孪生城市总体架构进一步迭代更新

经过五年来发展&#xff0c;数字孪生城市基本形成“三横四纵”的总体架构&#xff0c;“三横”为新型基础设施、智能运行中枢、孪生应用体系&#xff0c;“四纵”为组织保障体系、标准规范体系、网络安全防线、运营保障体系&#xff0c;具体如下。 数字孪生城市总体架构-来源&a…...

通过 Jetbrains GateWay实现Remote Development

本次环境准备 环境准备&#xff1a;win10、一台安装有树莓派系统的树莓派&#xff08;也可以是其他的服务器&#xff09; 第一步&#xff1a;通过官网下载JetBrains Gateway 官网地址&#xff1a;https://www.jetbrains.com/remote-development/gateway/ 第二步&#xff1a;安装…...

springboot 集成 lucene

简介 数据每分钟产生200条&#xff0c;使用mysql储存。目前有数据超过700M。按照日期查询&#xff0c;按月查询包含每次超过20w条以上&#xff0c;时间比较长。计划使用lucene优化查询&#xff0c;不适用es是因为项目较小&#xff0c;没有更富裕的资源。 基本步骤 引入依赖。…...

Android开机动画

Android开机动画 1、BootLoader开机图片2、Kernel开机图片3、系统启动时&#xff08;BootAnimation&#xff09;动画3.1 bootanimation.zip位置3.2 bootanimation启动3.3 SurfaceFlinger启动bootanimation3.4 播放开机动画playAnimation3.6 开机动画退出检测3.7 简易时序图 4、…...

vue中使用wow.js

一、安装 npm install wowjs --save-dev 二、main中引入 animate.css会自动安装 因为wow.js在animate.css基础上 main.js中引入animate.css import "animate.css" 三、 页面使用 有两种引入使用方式&#xff1a;1. import {WOW} from wowjs mounted() { n…...

网站edge -- 油猴 -> IDM

一、百度网盘限速 未解决 软件&#xff1a;IDM 安装路径&#xff1a; 1.1如果&#xff1a;edge 出问题打不开其他网站&#xff0c; 解决方法&#xff1a; 以管理员的身份&#xff0c;右击载这个软件&#xff0c;就好了 1.2使用这个软件 应该是右击这个软件 以管理员的身…...

Android片段

如果你希望应用根据不同的环境有不同的外观和行为&#xff0c;这种情况下就需要片段&#xff0c;片段是可以由不同活动重用的模块化代码组件。 片段&#xff08;Fragment&#xff09;是活动&#xff08;Activity&#xff09;的一种模块化部分&#xff0c;表示活动中的行为或界面…...

iOS实时监控与报警器

在现代信息化社会中&#xff0c;即使我们不在电脑前面也能随时获取到最新的数据。而苹果公司提供的iOS推送通知功能为我们带来了一种全新的方式——通过手机接收实时监控和报警信息。 首先让我们了解一下iOS推送通知。它是一个强大且灵活可定制化程度高、适用于各类应用场景&a…...

Git小白入门——上手实操之创建仓库和代码提交

版本库 什么是版本库呢&#xff1f;版本库又名仓库&#xff0c;英文名repository&#xff0c;简单理解成一个目录&#xff0c;目录里的所有文件都可以被Git管理&#xff0c;每个文件的修改、删除&#xff0c;Git都能跟踪&#xff0c;以便任何时刻都可以追踪历史&#xff0c;或…...

JS数组迭代方法实操

数组迭代方法有 1. every() 2.some() 3.foreach() 4.map() 5.filter 逐一操作&#xff0c;并简要区分之。 1 every() every() 方法使用指定的函数测试数组中所有的项&#xff0c;在数组的所有项都满足该条件时&#xff0c;才返回true&#xff0c;否则返回false&#xff1b; …...

基于snat+dnat发布内网K8S及Jenkins+gitlab+Harbor模拟CI/CD的综合项目

目录 项目名称 项目架构图 项目环境 项目概述 项目准备 项目步骤 一、修改每台主机的ip地址&#xff0c;同时设置永久关闭防火墙和selinux&#xff0c;修改好主机名&#xff0c;在firewalld服务器上开启路由功能并配置snat策略。 1. 在firewalld服务器上配置ip地址、设…...

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来 目录 时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现PSO-LSSVM时间序列预测未…...

java IO流(二) 字符流 缓冲流 原始流与缓冲流性能分析

字符流 前面学习的字节流虽然可以读取文件中的字节数据&#xff0c;但是如果文件中有中文&#xff0c;使用字节流来读取&#xff0c;就有可能读到半个汉字的情况&#xff0c;这样会导致乱码。虽然使用读取全部字节的方法不会出现乱码&#xff0c;但是如果文件过大又不太合适。…...

复现XSS漏洞及分析

XSS漏洞概述&#xff1a; 类型一&#xff1a;反射型 类型二&#xff1a;存储型 类型三&#xff1a;DOM型 复现20字符短域名绕过 一、安装BEEF 1、在Kali中运行apt install beef-xss 2、运行beef 3、在浏览器访问 二、安装galleryCMS *遇到一点小问题 提示"last…...

Vue组件之间传值

聊一聊vue里面组件之间的传值 首先总结一下vue里面传值的几种关系&#xff1a; 如上图所示, A与B、A与C、B与D、C与F组件之间是父子关系&#xff1b; B与C之间是兄弟关系&#xff1b;A与D、A与E之间是隔代关系&#xff1b; D与F是堂兄关系&#xff0c;针对以上关系 我们把组件…...

windows查看端口占用,通过端口找进程号(查找进程号),通过进程号定位应用名(查找应用)(netstat、tasklist)

文章目录 通过端口号查看进程号netstat通过进程号定位应用程序tasklist 通过端口号查看进程号netstat 在Windows系统中&#xff0c;可以使用 netstat 命令来查看端口的占用情况。以下是具体的步骤&#xff1a; 打开命令提示符&#xff08;CMD&#xff09;&#xff1a;按WinR组…...

Weblogic SSRF【漏洞复现】

文章目录 漏洞测试注入HTTP头&#xff0c;利用Redis反弹shell redis不能启动问题解决 Path : vulhub/weblogic/ssrf 编译及启动测试环境 docker compose up -dWeblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi…...