现在的架构
单机存在容量问题 需要进行水平扩展
表象:单机cpu使用率增高 memory占用增加 网络带宽使用增加
cpu的使用率看 cpu us和cpu sy两个参数 分别代表用户空间的CPU使用情况和内核空间的淳朴使用情况 分别主要是由用户层代码和系统调用引起的
另外还有load average参数 分别代表1,5,15分钟load平均值 0表示正常 1代表打满 1+代表等待阻塞(跟着cpu核数走 如果双核CPU则小于2即正常)
还有memory参数 free代表空闲内存 used代表使用内存
改进之后的部署图
需要4台服务器 Nginx、mysql和两台app 买就完了
将之前的那台服务器当做数据库服务器 将app的设置复制到两台应用服务器中
scp -r //var/www root@172.17.219.193:/var/
注意在启动之前要更改配置 更改数据库的url 允许远程连接 按这篇操作
然后到了Nginx的配置
首先需要知道Nginx的主要功能是
作为web服务器 存储静态资源的静态服务器
作为动静分离服务器
作为反向代理服务器 将动态请求反向代理到后端
修改前端代码 使得ajax动态请求能够正确地跳转
由于Nginx是由cpp写的 编译运行复杂 使用openresty框架 实现了大多数的配置和特性 并且可以自己选择Nginx相关的模块进行拓展
完成下载编译安装后将其设置为web服务器
sbin/nginx -c conf/nginx.conf
以后面这个nginx的配置文件的信息启动前面那个可执行文件
将静态资源放到nginx的html目录下 注意ajax跳转的url只能是公网ip 使用私网ip会出现跨域问题
接着实现nginx实现动静分离服务器的功能
location结点path特定的resources作为静态资源 其他路径作为动态资源
首先设置upstream server
设置动态请求location为proxy pass路径
开启tomcat access log验证
在现在的架构下 数据库服务器和应用服务器分开 但是在阿里德鲁伊连接池的管理下 建立了长连接 所以消耗较少
而nginx反向代理服务器和应用服务器之间使用的是默认的短连接 没有keepalive
修改nginx的配置文件 keepalive的时间 http的版本 和proxy_set_header Connection "";
Nginx高性能的原因
epoll多路复用 实现非阻塞式的io操作
master worker进程模型 允许平滑的重启 重新加载配置 而不会与客户端断开连接
协程机制 非阻塞式编程的机制 实现单进程单线程上又能实现并发的模型
epoll多路复用
Java bio模型 阻塞进程式 客户端与服务端之间建立长连接 发送socket的时候要将所有字节流都送到缓冲区才返回
linux select多路复用模型 变更触发轮询查找 减少了等待的时间消耗 发生变化时一定已经有数据可以处理
epoll模型 变更触发回调直接读取 理论上无上限
java的nio模型即借鉴了Linux select和epoll模型的设计思想
master-worker工作模型
master进程只管理管理员发送来的信号 而将客户端发送来的socket连接请求交给worker进程处理 平滑重启也是基于此的 将正在工作的worker进程的句柄回收 更改配置 新建worker进程 将句柄放入 整个过程在内存中进行 所以速度非常快 客户端不会感知
每个worker进程都是单线程的 引出了协程机制
协程机制
依附于线程的内存模型,切换开销小
遇阻塞及归还执行权,代码同步
无需加锁
会话管理机制
目前是默认的基于cookie传输sessionid的方式 在java tomcat容器session实现 在代码中的使用是otp验证码的保存
另外一种常见的方式是基于token传输类似sessionid的方式 java代码session实现
在分布式会话中
基于cookie传输sessionid:java tomcat容器session实现迁移到redis
基于token传输类似sessionid java代码session实现迁移到redis
后者用的多是因为在移动设备上的cookie会受到一些限制
基于cookie
由架构图可知 session是保存在对应tomcat的服务器上 而不是分布式集中管理的 所以可能从这边拿到验证码 动作又提交到了另外一边
在这部分使用redis的时候 不需要使用本地化磁盘持久保存的功能 只需要使用内存数据库实现缓存的功能即可
引入SpringBoot对redis的依赖
wget 官网上的链接 make、make install、 到src目录下./redis-server & ./redis-cli
将对象存入redis的时候需要将对应的类进行序列化 实现serializable接口
src/redis-server ./redis.conf
基于token
令牌的盖帘 下发令牌 存储在本地 使得下次请求中如果需要登录态 放在请求体中交给服务器
在usercontroler里面改一下 使用UUID(Universally Unique Identifier) 然后更改前端代码
//生成登录凭证token,UUID
String uuidToken = UUID.randomUUID().toString();
uuidToken = uuidToken.replace("-","");
//建议token和用户登陆态之间的联系
redisTemplate.opsForValue().set(uuidToken,userModel);
redisTemplate.expire(uuidToken,1, TimeUnit.HOURS);
// this.httpServletRequest.getSession().setAttribute("IS_LOGIN",true);
// this.httpServletRequest.getSession().setAttribute("LOGIN_USER",userModel);
//下发了token
return CommonReturnType.create(uuidToken);