内容简介:WEB应用开发完成后部署到Tomcat或其他容器中供用户访问. 小型应用在一台服务器上安装Tomcat并部署WEB应用. 随着访问量增大, Tomcat的压力会越来越大, 直至崩溃. 为了保证WEB应用的承载能力, 需要对WEB应用进行集群处理.技术发展到今天, 集群/负载均衡已经变的相对简单了. 下面用通俗的语言给刚入门的同学介绍下这两个概念:某KFC开业时只有一个点餐窗口(一台Tocmat服务器, 可以节约成本)对外提供点餐服务. 应对日常点餐没有问题, 当饭口或者周末时一个窗口就会排起长队(高并发)
WEB应用开发完成后部署到Tomcat或其他容器中供用户访问. 小型应用在一台服务器上安装Tomcat并部署WEB应用. 随着访问量增大, Tomcat的压力会越来越大, 直至崩溃. 为了保证WEB应用的承载能力, 需要对WEB应用进行集群处理.
技术发展到今天, 集群/负载均衡已经变的相对简单了. 下面用通俗的语言给刚入门的同学介绍下这两个概念:
某KFC开业时只有一个点餐窗口(一台Tocmat服务器, 可以节约成本)对外提供点餐服务. 应对日常点餐没有问题, 当饭口或者周末时一个窗口就会排起长队(高并发). 不仅顾客有怨言(请求响应时间长, 用户体验差), 服务员也会很累, 终于有一天他累倒了(Tomcat挂掉了).
这时在侧面增加了一个窗口(增加一台Tomcat服务器)提供点餐服务, 但是很多顾客不知道新窗口, 依旧在原有窗口排起了长队(用户依旧访问原来的Tomcat), 这时需要有一个人专门站在门口根据每个窗口的排队情况指引顾客去哪个窗口点餐(负载均衡器). 这个人作用是为了让各个窗口的点餐人数大致相等, 避免有的窗口很忙, 有的很闲. 随着顾客增加, 点餐窗口也会相应增加(Tomcat越来越多).
- 集群: 一群服务器集合在一起提供服务, 上例中多个点餐窗口(多台Tomcat)共同提供点餐服务就是集群.
- 负载均衡: 让集群中每个点餐窗口(每个Tomcat)的负载情况保持均衡, 不要出现某一个或几个太空闲.
两个概念是同时出现的, 没有集群的服务(单一Tomcat)也不存在负载均衡之说, 集群的服务没有负载均衡会浪费资源.
WEB负载均衡方案很多, Nginx
+ Tomcat
是常用的方案之一. Nginx作为负载均衡器根据每个Tomcat的负载情况进行分流.
- 每个Tomcat都相当于点餐窗口, 都可以提供点餐服务
- 每次要点餐都得先经过Nginx
- Nginx会根据每个窗口的空闲情况进行分配用户去哪个窗口点餐
- 第一次在1号窗口点餐, 点完后马上再次点餐, 有可能被分配到2号窗口
下面我们搭建负载均衡的WEB应用
1) 搭建WEB应用
准备WEB应用, 用两个Tomcat部署, 测试时为了能够区分请求是由哪个Tomcat进行处理, 将Tomcat端口号作为结果返回.
/** * 获取部署项目的Tomcat端口号 */ @RequestMapping("/port/get") @ResponseBody public String getPort(HttpServletRequest request) { return String.valueOf(request.getLocalPort()); } 复制代码
本例中分别使用 5677
, 5688
两个端口部署该项目. 访问 /port/get
请求返回结果为Tomcat的端口号
http:// localhost:5677/port/get http:// localhost:5688/port/get
2) Nginx配置负载均衡
Window下Nginx安装比较简单, 不会安装的同学自行百度, 简单介绍下Nginx配置文件: nginx.conf
# Nginx进程数 worker_processes 1; events { # 最大并发链接数 worker_connections 1024; } # Nginx处理HTTP请求相关的配置 # http不能重复, 全局唯一 http { # 虚拟主机, 可配置多个虚拟主机 # Nginx监听88,89,90三个端口, 可配置三个server server { # 端口号, 访问88端口会都按照该server下的配置进行处理 listen 88; # 主机名称 server_name localhost; # 根据正则表达式匹配URL, 匹配到的URL按照该location下的配置进行处理 # /代表访问88端口的所有请求 location / { # 静态资源所在根目录, 会从该目录下查找静态资源 # 例: 访问/a.html, 找到D:/a.html并返回 root D:/; } } } 复制代码
上述配置文件最基础的Nginx配置, 当我们访问 http://localhost:88
时会由Nginx处理, 下面我们配置Nginx的负载均衡.
- 配置1)中定义的两个tomcat, 在
http
节点下添加如下代码:
# 定义需要进行负载均衡的服务器信息 # upstream为关键字, springsession为自定义的名称 # server为关键字, 代表一个服务或服务(一个tomcat) # server的内容为服务器的信息, 形式为ip:端口 # weight定义了服务器负载的权重, 每4次请求有3次转发到5688, 1次到5677 upstream springsession { server localhost:5677 weight=1; server localhost:5688 weight=3; } 复制代码
- 配置当访问Nginx的所有请求转发至两个服务器处理
location / { # root D:/; # 转发至名称为springsession的upstream处理 proxy_pass http://springsession; } 复制代码
3) 测试负载均衡
访问 http://localhost:88/port/get
, Nginx将请求转发至两台tomcat中的一个进行处理. 可以发现请求返回的结果是不一样的
- 根据配置的权重, 每4次访问有3次由
5688
上, 1次由5677
处理. - 权重配置只是最终平均值为3/4和1/4, 不一定是前三次访问都会由
5688
处理. - 不配置weight时, 一次请求两个tomcat被分配到的概率各占50%
负载均衡配置好了, 有这样一个问题:
你在1号窗口点餐时把钥匙暂存到该窗口, 下次在点餐可能被分配到2号窗口或其他窗口(也有可能分配到1号窗口), 那么在其他窗口取钥匙显然是行不通的. 因为其他窗口没有你的钥匙. 这时你只能祈祷能快速把你分配到1号窗口.
如果保存钥匙的操作变为在SESSION中保存信息, 那么当你的请求被 Tomcat1
处理时, Tomcat1
会为你生成一个SESSION, 你在SESSION里面设置了信息, 下次你的请求被分配到 Tomcat2
处理, Tomcat2
又会为你生成一个SESSION. 这是两个独立的并且不共享的SESSION. 因此你是不可能的在 Tomcat2
中获取你在 Tomcat1
中保存的信息.
登录的原理其实就是在SESSION中保存登录状态, 按照上面的分析, 登录在集群部署的服务中就失效了. 在Tomcat1中登录, 下次访问Tomcat2, 此时通过SESSION判断登录状态得到的一定是未登录, 还需要再次登录. 用户疯, 你疯, 老板也会疯...
如果有一个公用位置用来存放东西, 所有的点餐窗口都在公用位置存取顾客物品, 上面的问题就迎刃而解了.
这就是本文重点: 统一管理集群下各WEB应用的SESSION .
-
容器的选择: 我们需要一个能够统一存放SESSION的容器. 从以下3点分析,
Redis
无疑是最合适的. SESSION是经常被读取的, 因此数据库, 文件系统均不适合, 最好是从内存操作. SESSION是有ID的, 一个ID对应一个SESSION, 最好是一个K/V的容器 SESSION是有时效性的(时间长不用, 需要删除). 最好能够设置过期时间 -
SESSION存取机制: 由于SESSION是Tomcat生成的, 因此首先想到的是修改Tomcat的SESSION机制, 从
Redis
中存取SESSION, 这样会带来一个问题, 假设Tocmat升级了, 我们还需要重新对Tomcat进行修改. 因此这个方案可行性较差. 我们可以这样考虑, 即使Tomcat生成了SESSION, 我们也是在WEB应用中使用, 为什么不在WEB应用中重新生成一个SESSION呢, 编写这样一个过滤器, 在进入WEB应用之前, 抛弃Tomcat的SESSION, 从Redis
中获取SESSION.
恰巧有这样一个框架帮助我们完成上面的想法, 只需要配置一下即可实现统一管理SESSION. 他就是 Spring Session .
为了对 Spring Session
的功能印象深刻, 我们先来测试一下没有Spring Session时我们的集群应用是怎样处理SESSION的
把我们负载均衡的WEB应用中增加一个控制器方法, 把每次的 SESSION ID
输出一下.
/** * 获取部署项目的SESSION ID */ @RequestMapping("/sessionid/get") @ResponseBody public String getPort(HttpServletRequest request, HttpSession session) { int port = request.getLocalPort(); // 端口 String sessionId = request.getSession().getId(); // SESSION ID return "port: " + port + ", session id: " + sessionId; } 复制代码
启动项目, 多次访问 http://localhost:88/sessionid/get
SESSOIN ID SESSION ID SESSION ID
出现上述情况的原因如下:
- 访问
5677
, 由于没有SESSION,Tomcat5677
生成SESSION, ID为1
, 并将1
返回客户端 - 访问
5677
, 浏览器携带SESSION_ID=1
,Tomcat5677
找到对应的SESSION. 因此SESSION_ID为1
- 访问
5688
, 浏览器携带SESSION_ID=1
,Tomcat5688
找不到对应的SESSION, 重新生成SESSION, ID为2
, 并将2
返回客户端 - 访问
5677
, 浏览器携带SESSION_ID=2
,Tomcat5677
找不到对应的SESSION, 重新生成SESSION, ID为3
, 并将3
返回客户端 - 访问
5688
, 浏览器携带SESSION_ID=3
,Tomcat5688
找不到对应的SESSION, 重新生成SESSION, ID为4
, 并将4
返回客户端
4) 统一SESSION管理
下面我们来用 Spring Session
来管理WEB应用的SESSION
1) 安装 Redis 并开启
参见文章 http://www.cnblogs.com/jaign/articles/7920588.html
2) 添加Spring Session依赖
// Spring Session依赖 "org.springframework.session:spring-session-data-redis:2.0.5.RELEASE", // Redis依赖 "io.lettuce:lettuce-core:5.0.4.RELEASE" 复制代码
3) 配置Spring Session过滤器
在 Web.xml
中配置 Spring Session
提供的过滤器, 该过滤器主要负责将Tomcat生成的SESSION替换成Redis中保存的SESSION.
<!-- Spring Session过滤器 --> <!-- 负责在进入WEB应用之前将Tomcat生成的SESSION替换为Redis中的SESSION --> <filter> <filter-name>springSessionRepositoryFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSessionRepositoryFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 复制代码
4) SpringSession/Redis配置
在Spring配置文件中增加 Spring Session
配置和 Redis
配置
beans { xmlns context: "http://www.springframework.org/schema/context" // 启动注解方式 context.'annotation-config'() // 配置Spring Session // 实际上是配置Web.xml中使用的Spring Session过滤器 // 将Tomcat的Session替换为Redis中管理的Session sessionConfig(RedisHttpSessionConfiguration) // 配置Redis客户端连接 // 默认连接本地6379端口 lettuce(LettuceConnectionFactory) } 复制代码
5) 测试
启动项目, 多次访问 http://localhost:88/sessionid/get
, 无论如何访问 SESSION ID
都是一样的.
同时 Redis
中也出现了当前SESSION的记录.
使用 Spring Session
后访问集群下的WEB应用时SESSION处理过程:
- 访问
5677
, 由于Redis
中没有SESSION
, 因此会生成一个SESSION
并存入Redis
, ID为1
, 并将1
返回客户端 - 访问
5677
, 浏览器携带SESSION_ID=1
,Tomcat5677
在Redis
中找到了SESSION
. 因此SESSION_ID
为1
- 访问
5688
, 浏览器携带SESSION_ID=1
,Tomcat5688
在Redis
中找到了SESSION
. 因此SESSION_ID
为1
- 清除
Redis
, 再次访问5677
, 由于Redis
中没有ID为1
的SESSION
, 因此会重新生成, ID也相应变化了
5) 示例代码
此时我们已经实现了统一管理SESSION, 无论访问任一TOMCAT都可以找到相同的SESSION.
当我们的应用进行集群后, 统一管理SESSION势在必行, 实现统一管理SESSION的方式很多, 本文只是其中一种方式. 重在让同学们理解统一管理SESSION的重要性和他的基本原理.
https://github.com/atd681/alldemo atd681-springsession
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Tomcat集群之会话保持
- Tomcat集群之会话保持
- 如何解决分布式集群会话?
- jboss-as-7.1.0.CR1b域集群和会话复制环境部署 荐
- java – Spring会话数据Redis – 从Redis Store获取有效会话,当前用户
- google-app-engine – GAE webapp2会话:创建和检查会话的正确过程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
文明之光(第二册)
吴军 / 人民邮电出版社 / 2014-6 / 59.00元
【《文明之光》系列荣获由中宣部、中国图书评论学会和中央电视台联合推选的2014“中国好书”奖】 吴军博士从对人类文明产生了重大影响却在过去被忽略的历史故事里,选择了有意思的几十个片段特写,以人文和科技、经济结合的视角,有机地展现了一幅人类文明发展的宏大画卷。 《文明之光 》系列大致按照从地球诞生到近现代的顺序讲述了人类文明进程的各个阶段,每个章节相对独立,全景式地展现了人类文明发展历程......一起来看看 《文明之光(第二册)》 这本书的介绍吧!