一. web
1.1 为什么“网页里的应用”≈“Tomcat 里的应用”?
浏览器只认识 HTTP。
你在地址栏敲 http://xxx.com/login,浏览器发出去的就是一段纯文本 HTTP 报文。
Tomcat 天生吃 HTTP。
它实现了 JavaEE 里的 Servlet 规范:把 HTTP 请求封装成 HttpServletRequest → 交给程序员写的 LoginServlet → 生成 HTML → 再按 HTTP 格式返回。
于是“浏览器 ↔ HTTP ↔ Tomcat ↔ 你的业务代码”这一条路就打通了。
因此,只要最终是通过浏览器访问的,几乎都可以说“由 Tomcat(或同类 Web 服务器)实现”。
1.2 B/S 与 C/S 的“空间之争”
客户端 | 必须安装专门软件(QQ、Steam、银行 U 盾) | 只用浏览器 |
升级 | 每台电脑都要重新装包 | 改服务器就行 |
磁盘占用 | 客户端几百 MB 起步 | 0 MB(浏览器不算) |
网络协议 | 随意(TCP/UDP、自定义) | 只用 HTTP/HTTPS |
代表场景 | 游戏、实时交易 | 论坛、OA、博客、后台管理 |
结论:
“节省磁盘空间”只是 B/S 最直观的优势,真正让它统治世界的是 零安装、零维护、跨平台。
1.3、三大核心技术到底各干什么?
HTML(骨架)
纯文本的标签积木:<form>、<input>、<table>……告诉浏览器“这里有个登录框,那里放个按钮”。
CSS(皮肤)
同一段 HTML,可以换不同 CSS 立刻变成“极简风”或“赛博朋克”。
例:
css
复制
input[type=text] {
border-radius: 8px;
box-shadow: 0 0 10px #00f;
}
JavaScript(肌肉)
让页面“动起来”:
-
表单实时校验(失焦就提示“密码太短”)
-
Ajax 异步拉数据(下面细讲)
-
动画、游戏、可视化……
浏览器拿到 .html → 边解析边向服务器要 .css、.js → 渲染引擎把三者拼成你看到的像素。
1.4、同步交互:整页刷新之痛
经典场景:注册页面
流程:
填 8 个字段 → 点【提交】
浏览器整页 POST 给服务器
服务器发现“两次密码不一致”
返回一个全新 HTML 页面
浏览器重新渲染 → 前 7 个字段全清空
这就是“同步”——一次 HTTP 请求对应一次完整页面刷新。
缺点:
-
带宽浪费(CSS/JS/图片全部重传)
-
体验差(用户想骂人)
1.5、异步交互:Ajax 的魔法
关键技术:XMLHttpRequest(浏览器内置 API)。
浏览器悄悄再开一条 HTTP 连接,只拿回 需要的那一小块数据(JSON/HTML 片段),然后用 JS 把页面局部更新。
用户无感刷新。
流程对比:
-
同步:填表单 → 提交 → 等待 → 整页刷新
-
异步:输完邮箱 → JS 立即发请求 /checkEmail?email=xxx → 返回 {"ok":false,"msg":"已注册"} → 页面只把提示字变红,其他不动
关键词:
-
Ajax(Asynchronous JavaScript And XML)
-
现在多用 JSON 代替 XML
-
衍生:Fetch API、Axios、jQuery.ajax()
1.6、从“代码”到“网页”的完整链路
你在 IDEA 写 LoginServlet.java,继承 HttpServlet,处理 POST /login。
mvn package 打成 war → 扔到 Tomcat 的 webapps。
浏览器访问 http://域名/login.html。
Tomcat 把 login.html 吐给浏览器(HTML+CSS+JS)。
用户填完账号密码,JS 用 Ajax POST /login。
LoginServlet 查询数据库 → 返回 JSON {status:0,token:xxx}。
JS 把 token 存 localStorage,页面跳转到 /index.html,全程无整页刷新。
二 tomcat
#安装java环境
[root@tomcat ~]# yum install java-1.8.0-openjdk.x86_64 -y
#查看java版本
[root@tomcatA ~]# java -version
openjdk version "1.8.0_402"
OpenJDK Runtime Environment (build 1.8.0_402-b06)
OpenJDK 64-Bit Server VM (build 25.402-b06, mixed mode)
#查看java的环境目录
[root@tomcatA ~]# which java
/usr/bin/java
[root@tomcatA ~]# ll /usr/bin/java
lrwxrwxrwx 1 root root 22 Jul 30 10:41 /usr/bin/java -> /etc/alternatives/java
#java的运行环境
[root@tomcatA ~]# cd /etc/alternatives/jre
[root@tomcatA jre]# ls
ASSEMBLY_EXCEPTION bin lib LICENSE THIRD_PARTY_README
#解压并生成tomcat的程序目录
[root@tomcatA ~]# tar zxf apache-tomcat-9.0.107.tar.gz -C /usr/local/
[root@tomcatA ~]# cd /usr/local/
[root@tomcatA local]# ls
apache-tomcat-9.0.107 bin etc games include lib lib64 libexec sbin share
src
[root@tomcatA local]# mv apache-tomcat-9.0.107/ tomcat
[root@tomcatA local]# ls
bin etc games include lib lib64 libexec sbin share src tomcat
#启动tomcat
[root@tomcatA local]# cd tomcat/
[root@tomcatA tomcat]# cd bin/
[root@tomcatA bin]# ls
bootstrap.jar
commons-daemon-native.tar.gz makebase.sh
juli.jar
catalina.bat
native.tar.gz
catalina.sh
configtest.bat
configtest.sh
wrapper.bat
catalina-tasks.xml daemon.sh
wrapper.sh
ciphers.bat
ciphers.sh
digest.bat
digest.sh
commons-daemon.jar makebase.bat
tomcat
setclasspath.bat tomcat
setclasspath.sh
shutdown.bat
shutdown.sh
startup.bat
startup.sh
[root@tomcatA bin]# ./startup.sh
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME:
/usr
tool
tool
version.bat
version.sh
Using CLASSPATH:
/usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.
[root@tomcat ~]# netstat -antlupe | grep java
安装JAVA环境
java版本查看
查看java真正的环境目录
jre真正的运行环境
要把java的运行环境指定给Tomcat
安装tomcat:
解压并重命名
进入目录并启动它
启动成功后查看端口
测试:
在另一台主机上也要做
简单一点就直接复制了
编写启动程序
创建专用系统用户
useradd -r -s /sbin/nologin -M tomcat
统一目录权限
chown -R tomcat:tomcat /usr/local/tomcat
写入环境变量(Tomcat 专用)
文件:/usr/local/tomcat/conf/tomcat.conf
内容只有一行:
JAVA_HOME=/etc/alternatives/jre
生成 systemd 单元文件
文件:/lib/systemd/system/tomcat.service
[Unit]
Description=Apache Tomcat
After=syslog.target network.target
[Service]
Type=forking
EnvironmentFile=/usr/local/tomcat/conf/tomcat.conf
ExecStart=/usr/local/tomcat/bin/startup.sh
ExecStop=/usr/local/tomcat/bin/shutdown.sh
PrivateTmp=true
User=tomcat
Group=tomcat
[Install]
WantedBy=multi-user.target
启动并设为开机自启
systemctl daemon-reload
systemctl enable –now tomcat # 立即启动 + 开机自启
systemctl status tomcat # 查看运行状态
一键验证
ss -lntp | grep java # 应看到 8080 端口
curl -I http://localhost:8080 # 返回 200 OK
内容中制定用户了,所以这里要建立用户
要给tomcat指定java的运行环境
报错情况:
此时是没有运行这个文件的权限
查看权限
加权限
测试:
2.2 结合反向代理实现tomcat部署
浏览器里看到的“网页应用”≈ Nginx + Tomcat 这对黄金搭档在干活,而 Nginx 负责反向代理 / 负载均衡,Tomcat 负责跑 Java 代码。
为了不丢登录态,还要解决 Session 一致性 问题。
什么是“反向代理”?
-
正反向区别一句话:
-
正向代理:帮 客户端 去访问外网(翻墙)。
-
反向代理:帮 服务端 接收外网请求(统一入口)。
-
-
为什么要反向代理 Tomcat?
-
端口统一:浏览器只认 80/443,Tomcat 默认 8080。
-
动静分离:Nginx 处理静态文件(css/js/png)速度是 Tomcat 的 10 倍。
-
安全:Tomcat 不再直接暴露,可放在内网。
-
指令拆解
nginx
复制
location ~ \\.jsp$ {
proxy_pass http://172.25.254.10:8080; # 把请求原封不动转给 Tomcat
proxy_set_header Host $host; # 保留浏览器里的域名,否则 Tomcat 重定向会出错
}浏览器发的是 Host: lee.timinglee.org,Tomcat 也收到同样的 Host,就不会跳错地址。
为什么要“负载均衡”?
-
单台 Tomcat 的并发瓶颈:
纯 Java 业务,QPS 大约几百~一两千。用户量一上来就卡死。 -
思路:把同样的应用复制到多台机器,把压力 分摊,术语叫 Load Balance。
-
Nginx 的 3 种常用调度算法
-
轮询(默认)——每人一次,最公平。
-
ip_hash ——同一 IP 固定到一台,解决 会话保持(后面细说坑)。
-
hash $cookie_JSESSIONID ——按 SessionID 选机器,比 ip_hash 更精准。
HTTP 的无状态 & Session 机制
-
无状态:HTTP 协议本身不带“记忆”。
第一次请求登录成功,第二次刷新页面,Tomcat 压根不知道你是刚才那个人。 -
解决思路:
-
服务器给浏览器发一张“身份证”—— SessionID(随机字符串)。
-
浏览器以后每次请求都在 Cookie 里带上 JSESSIONID=xxx。
-
Tomcat 收到后,从自己 内存 里找对应的 SessionMap,取出用户信息。
-
生命周期
-
默认 30 min 没交互就失效(web.xml 可调)。
-
关闭浏览器 Cookie 消失,再开就重新领身份证。
-
负载均衡后出现的新问题:Session 丢失
场景:
-
浏览器第一次请求被分到 Tomcat-A,SessionID=1001 存在 A 的内存。
-
第二次请求被分到 Tomcat-B,B 内存里没 1001 → 强制跳回登录页。
这就是“找不到 SessionID”的根本原因:HTTP 无状态 + 多台机器内存不共享。
三种 Session 一致性方案对比
表格
复制
1. ip_hash | Nginx 按客户端 IP 固定到同一台机器 | 零改动 | NAT 场景下严重倾斜;单点故障需重新登录 | 内部后台、用户量小 |
2. Tomcat Cluster | 多台 Tomcat 组播同步 Session | 官方原生 | 组播受网络限制;同步风暴 | 小规模内网 |
3. MSM+Memcached | Session 序列化后存 Memcached,多台 Tomcat 共享 | 无状态、高可用、扩展好 | 需额外部署 Memcached;首次配置稍复杂 | 生产最强 |
MSM+Memcached 工作流程(图+文字)
复制
浏览器
│
│ 1. 登录请求 → Nginx → Tomcat-A
│ │
│ └─ 2. 创建 SessionID=1001
│ 同时 3. 序列化 Session 到 Memcached
│
│ 4. 第二次请求 → Nginx → Tomcat-B
│ │
│ └─ 5. Tomcat-B 发现 Cookie:JSESSIONID=1001
6. 去 Memcached 取 Session → 继续业务
-
如果 Tomcat-A 挂了,Tomcat-B/C/D 任何一台都能从 Memcached 拿到同一份 Session,用户无感知。
配置小结(回顾)
反向代理(单机)
nginx
复制
location ~ \\.jsp$ {
proxy_pass http://172.25.254.10:8080;
proxy_set_header Host $host;
}
负载均衡(多机)
nginx
复制
upstream tomcat {
hash $cookie_JSESSIONID; # 会话保持最稳
server 172.25.254.10:8080;
server 172.25.254.20:8080;
}
MSM 共享 Session
-
把相关 jar 扔进 $CATALINA_HOME/lib
-
context.xml 指定 Memcached 节点
-
重启 Tomcat 即可生效单机
-
现在需要一个测试页面
测试页面不能直接在root里要放到默认发布目录里
直接下载nginx
在配置文件里写下子配置命令 include
错误情况:
访问超时
直接访问没问题
域名解析有误,之前实验的影响
测试:
之前的信息也会保留
这个代码,nginx不能写,是tomcat来写的
tomcat挂了,这个实验就不能用了
单机情况下,tomcat挂了,就不能用了
要解决这个问题,用多机来做
多机
测试:
会话绑定
缺点:如果同一个路由器过来的路由都会跑向一个路由器上了
cookie客户端生成的会话 session服务器生成的会话
会话:以用户的身份与服务器对话
此时的情况时,来一个服务器就重新对话
所以解决办法是生成sessionIP要与cookieIP一致,cookie值变了,就访问不同服务器
写cookie哈希
对键值进行哈希
测试:
ID是一样的就会访问同一个服务器上
只有挂了浏览器才会改变,此时ID也变了
前端还在做数据存储,但是后端的tomcat却挂了,前端是不知道的,该怎么办?
无论是10还是20,之前的数据该怎么保存?
三、Memcached
Memcached 只支持能序列化的数据类型,不支持持久化,基于Key-Value的内存缓存系统memcached 虽然没有像redis所具备的数据持久化功能,比如RDB和AOF都没有,但是可以通过做集群同步的方式, 让各memcached服务器的数据进行同步,从而实现数据的一致性,即保证各memcached的数据是一样 的,即使有任何一台 memcached 发生故障,只要集群中有一台 memcached 可用就不会出现数据丢 失,当其他memcached 重新加入到集群的时候,可以自动从有数据的memcached 当中自动获取数据并 提供服务。
Memcached 借助了操作系统的 libevent 工具做高效的读写。libevent是个程序库,它将Linux的epoll、 BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥高 性能。
memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能 Memcached 支持最大的内存存储对象为1M,超过1M的数据可以使用客户端压缩或拆分报包放到多个 key中,比较大的数据在进行读取的时候需要消耗的时间比较长,
memcached 最适合保存用户的 session实现session共享 Memcached存储数据时, Memcached会去申请1MB的内存, 把该块内存称为一个slab, 也称为一个page Memcached 支持多种开发语言,包括:JAVA,C,Python,PHP,C#,Ruby,Perl等
Memcached 官网: http://memcached.org/
它本身不支持持久化,只支持能序列化的数据类型
没有客户端,是个键值
[root@tomcat ~]# yum install memcached -y
[root@tomcat ~]# vim /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS="-l 0.0.0.0,::1"
0
980
[root@tomcat ~]# systemctl enable –now memcached
[root@tomcat ~]# netstat -antlupe | grep memcache
tcp
0 0.0.0.0:11211
97815
34711/memcached
[root@tomcat ~]# telnet localhost 11211
Trying ::1…
Connected to localhost.
Escape character is '^]'.
#增加
add leekey 0 60 4
#0 是否压缩 60 过期时间 4 字长
test
STORED
add leekey1 0 60 3
lee
STORED
#查看
get leekey
VALUE leekey 0 4
test
get leekey1
VALUE leekey1 0 3
lee
#改
set leekey 0 60 5
test1
STORED
get leekey
VALUE leekey 0 5
test1
END
add leekey1 0 60 4
test
#删除
delete leekey
DELETED
get leekey
END
get leekey1
VALUE leekey1 0 3
lee
#清空
flush_all
OK
get leekey1
END
实验:
修改接口,打开全部
再次查看端口信息
要把接口都打开
测试(长连接实验也用过):
这里的2是字长 :规定后面输入的字数只能是两个
查看刚刚储存的内容
修改储存的内容
删除并退出
企业一般不用
使用插件,让tomcat往memcached里存储,实际上,两者是没有关系的。
四、msm
[root@tomcat-1 ~]# vim /usr/local/tomcat/conf/context.xml
@@@@内容省略@@@@
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:172.25.254.10:11211,n2:172.25.254.20:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFacto
ry"
/>
[root@tomcat-2 tomcat]# vim /usr/local/tomcat/conf/context.xml
@@@@内容省略@@@@
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:172.25.254.10:11211,n2:172.25.254.20:11211"
failoverNodes="n2"
requestUriIgnorePattern=".*\\.(ico|png|gif|jpg|css|js)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFacto
ry"
/>
修改nginx配置
[root@Nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conf
upstream tomcat {
hash $cookie_JSESSIONID;
server 172.25.254.10:8080;
server 172.25.254.20:8080;
}
server {
listen 80;
server_name lee.timinglee.org;
root /webdataw/nginx/timinglee.org/lee;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
try_files $uri $uri.html $uri/index.html /error/default.html;
}
location ~ \\.jsp$ {
proxy_pass http://tomcat;
}
查看memcached的版本
要去找合适版本的插件
安装
要把插件放进tomcat库里
查看tomcat的插件存储位置
服务重启成功后IP会发生变化
failoverNodes:当一个服务器的tomcat出故障时,另一台会要找自己的memcached的,因为自己的里面也是有对端的数据的
重启服务
测试:
20tomcat挂了
这里会有之前数据的原因是,20往10的memcached里存的数据,20挂了,10会在memcached里读取数据
评论前必须登录!
注册