nginx+tomcat+memcached集群

Nginx+Tomcat+MemCached集群配置

一、Nginx 介绍
Nginx (发音同 engine x)是一款高性能的 HTTP 和 反向代理 服务器国内使用nginx Web服务器的网站有:新浪、网易、 腾讯等。

二、Tomcat 介绍
Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache、Sun 和其他一些公司及个人共同开发而成。由于有了Sun 的参与和支持,最新的Servlet 和JSP 规范总是能在Tomcat 中得到体现,Tomcat 5支持最新的Servlet 2.4 和JSP 2.0 规范。因为Tomcat 技术先进、性能稳定,而且免费,因而深受Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web 应用服务器。目前最新版本是8.0。

三、MemCached 介绍
Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的hashmap。其守护进程(daemon )是用C写的,但是客户端可以用任何语言来编写,并通过memcached协议与守护进程通信。

四、实验环境:物理机:192.168.126.1 win7

      虚拟机: 192.168.126.15 CentOS-5.5  

      在物理主机上安装Nginx服务,memcached服务,配置Tomcat-台端口8080

      在虚拟机上配置一台Tomcat 端口8080

      熟悉Linux的朋友也可在Linux上搭建Nginx + memcached 服务


download Tomcat: http://mirror.esocc.com/apache/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54-windows-x86.zip

五、准备一个java web小项目测试本人此处项目名为Session

 将些项目发布到物理机Tomcat 192.168.126.1:8080 和虚拟机Tomcat 192.168.126.15:8080 注意端口不能是80,80端口由nginx服务器代理。


 项目结构:一个实体User类,一个LoginServlet类处理登录请求,一个login.jsp登录页面,一个success.jsp登录成功页面
package com.rx.vo;

import java.io.Serializable;

public class User implements Serializable{        

    private static final long serialVersionUID = 7073950136835429991L;

    private String userId;

    private String username;

    private String password;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public User() {
        super();
        // TODO Auto-generated constructor stub
    }

    public User(String userId, String username, String password) {
        super();
        this.userId = userId;
        this.username = username;
        this.password = password;
    }


}

package com.rx.servlet;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.rx.vo.User;

@WebServlet(name = "LoginServlet" , urlPatterns = "/loginServlet.do")
public class LoginServlet extends HttpServlet{

    /**
     * 
     */
    private static final long serialVersionUID = -6451894481175083005L;

    private static Map<String,User> users = new HashMap<String,User>();

    private static Log LOG = LogFactory.getLog(LoginServlet.class);

    static {
        users.put("tom", new User("1001", "tom", "123456"));
        users.put("king", new User("1002", "king", "123456"));
        users.put("sam", new User("1003", "sam", "123456"));
        users.put("jack", new User("1004", "jack", "123456"));
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

         String userName = request.getParameter("userName");  
         String password = request.getParameter("password");  
         LOG.info("Raw input:userName=" + userName + ",password=" + password);  
         String url = request.getContextPath();
         LOG.info("URL------>>" + url);
         if(userName!=null && password!=null  
                   && users.containsKey(userName)  
                   && users.get(userName).getPassword().equals(password)) {  
              LOG.info("Login;status=SUCCESS");  
              HttpSession session = request.getSession();
              session.setAttribute("SESSION_USER", users.get(userName));
              LOG.info("SessionID----->>" + session.getId());
              response.sendRedirect(url + "/success.jsp");
         } else {  
              LOG.info("Login;status=FAIL");  
              request.getRequestDispatcher("login.jsp").forward(request, response);  
         }  
    }
}
login.jsp页面内容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"  
     pageEncoding="UTF-8"%>  
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">  
<html>  
<head>  
<title>登录页面</title>  
<meta http-equiv="pragma" content="no-cache">  
<meta http-equiv="cache-control" content="no-cache">  
<meta http-equiv="expires" content="0">  
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">  
<meta http-equiv="description" content="This is my page">  
<link rel="stylesheet" type="text/css" href="css/main.css"></link>
</head>  
<%
    String ip = request.getLocalAddr();
    String id = session.getId();
%>
<body>  

 <form method="post" action="loginServlet.do">
        <table class="table">   
            <tr>
                <td colspan="2">                    
                    <b>SESSION_ID = <%=id %></b>                
                </td>
            </tr>       
            <tr>        
                <td height="15" colspan="2">
                    <b><%=ip %> 用户登录</b>
                </td>
            </tr>
            <tr>
                <td width="80"><b>用户名</b></td>
                <td><input type="text" name="userName" value="" /></td>
            </tr>
            <tr>
                <td><b>密 码</b></td>
                <td><input type="password" name="password" value="" /></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="登 录" />
                    <input type="reset" value="重 置" />
                </td>
            </tr>
        </table>
    </form>  
</body>  
</html>  


success.jsp内容如下:
<%@ page language="java" import="com.rx.vo.*" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录成功页面</title>
<link rel="stylesheet" type="text/css" href="css/main.css"></link>
</head>

<%  
    User user = (User) session.getAttribute("SESSION_USER");  
    String ip = request.getLocalAddr();
    String id = session.getId();
    String username = "";
    if (user != null) {
        username = user.getUserId() + "------->>" + user.getUsername() + "----->>" + user.getPassword();
    }
%>  
<body>    
     <h2 class="title"><%=ip %> 服务器处理结果 SESSIONID = <%=id %> </h2> 
     <h2 class="title">当前登录用户:<%=username %> <a href="index.html">主页</a></h2>      
</body>  
</html>           

六、download Nginx: http://nginx.org/download/nginx-1.6.0.zip 目前最新版本是nginx-1.7.1
并解压到E:\nginx-1.6.0目录中,在命令提示窗口中进入到E:\nginx-1.6.0目录
nginx.exe #启动nginx服务
nginx -s stop #停止nginx服务
nginx -s reload #重新启动nginx
nginx -s quit #退出nginx
nginx -t #检测配置文件的正确性
启动Nginx后输入http://127.0.0.1/ 可以看到nginx的欢迎页面了,非常友好

接下来配置配置Nginx负载均衡,我的目录在E:\nginx-1.6.0\conf\nginx.conf以下是我简单配置

#是注释内容,配置文件修改完成后启动两台Tomcat服务,然后重新启动Nginx服务。
输入:http://127.0.0.1/Session/login.jsp
响应结果:
192.168.126.1 用户登录
再次刷新:
192.168.126.15 用户登录
证明Nginx反向代理和负载均衡配置成功了。

#user nobody; #用户
worker_processes 1; #工作进程,根据硬件调整,大于等于CPU核数

#error_log logs/error.log; #错误日志配置

#error_log logs/error.log notice;

#error_log logs/error.log info;

#pid logs/nginx.pid; #pid放置位置

events {

#use epoll;  

#使用epoll的I/O 模型

# 补充说明:

#与apache相类,nginx针对不同的操作系统,有不同的事件模型

#A)标准事件模型

#Select、poll属于标准事件模型,如果当前系统不存在更有效的方法,nginx会选择select或poll

#B)高效事件模型

#Kqueue:使用于FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X.使用双处理器的MacOS #X系统使用kqueue可能会造成内核崩溃。

#Epoll:使用于Linux内核2.6版本及以后的系统。

#/dev/poll:使用于Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。

#Eventport:使用于Solaris 10. 为了防止出现内核崩溃的问题, 有必要安装安全补丁  

worker_connections  1024;

#工作进程的最大连接数量,根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行

#每个进程允许的最多连接数, 理论上每台nginx服务器的最大连接数为worker_processes*worker_connections

keepalive_timeout 60; 

}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
include mime.types;
default_type application/octet-stream;

sendfile        on;
#tcp_nopush     on;

#keepalive_timeout  0;
keepalive_timeout  65;

#gzip  on;

#配置两台Tomcat真实服务器
upstream  B2C_WEB {      
  #ip_hash;     #目的保证客户端的ip地址不变,请求就只送到固定的一个Tomcat处理

  server 192.168.126.1:8080 weight=1 max_fails=3 fail_timeout=30s;       
  server 192.168.126.15:8080 weight=1 max_fails=3 fail_timeout=30s;

  #weight=1表示
  #这表示,如果服务器192.168.126.1 | 15在30秒内出现了3次错误,
  #那么就认为这个服务器工作不正常,从而在接下来的30秒内nginx不再去访问这个服务器。
}  

server {
    listen       80; #监听端口
    server_name  localhost;
    charset UTF-8;          
    index index.html index.htm index.jsp index.do;
    root E:\\JavaEE\\Apache-Tomcat-7.0.41\\wtpwebapps; #WEB服务的主目录

    location ~ ^/(WEB-INF)/ { 
      deny all; #禁止访问WEB-INF目录
    }            

    # 动态数据由代理服务器Tomcat处理
    location ~ .*\.(jsp|jspx|do)?$ { 
      proxy_pass http://B2C;# 反向代理         
      proxy_set_header  X-Real-IP $remote_addr;
      proxy_next_upstream http_502 http_504 error timeout invalid_header;
   }     

   # 静态数据直接读取不经过Tomcat服务器
   location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
   {        
     expires      30d; #在客户端保存时间
   }
   location ~ .*\.(js|css)?$
   {        
     expires      1h;
   }

   # 监控Nginx服务状态
   location /Nginxstatus {
     stub_status on;
     access_log   off;
   }        

    #charset koi8-r;

    #access_log  logs/host.access.log  main;

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }

}

}
七、download Memcached: 官方并未提供memcached windows版本 自己到网上找memcached-1.2.6-win32-bin.zip或
memcached-win32-1.4.4-14.zip下载

八、memcached安装步骤:
①.解压到指定目录,如E:\memcached\
②.打开 开始 —>> 运行 —>> cmd 调出命令窗口,进入到memcached的解压目录
③.安装memcached: 输入E:\memcached\memcached.exe -d install 执行安装
注意事项根据下载版本不同安装文件可能是单文件,不用质疑。
④.启动memcached服务:输入E:\memcached\memcached.exe -d start 启动服务

设置memcached,启动该服务后,memcached服务默认占用的端口是11211,占用的最大内存默认是64M。

如果需要修改这两个参数,比如修改端口为10000,内存为512,则输入:

E:\memcached\memcached.exe -p 10000 -m 512 -d start , -p 表示要修改的端口,-m表示占用的最大内存(单位为M)。

⑤.参数介绍在安装和启动时可设置如下参数:

-p 监听的端口
-l 连接的IP地址, 默认是本机
-d start 启动memcached服务
-d restart 重起memcached服务
-d stop|shutdown 关闭正在运行的memcached服务
-d install 安装memcached服务
-d uninstall 卸载memcached服务
-u 以的身份运行 (仅在以root运行的时候有效)
-m 最大内存使用,单位MB。默认64MB
-M 内存耗尽时返回错误,而不是删除项
-c 最大同时连接数,默认是1024
-f 块大小增长因子,默认是1.25
-n 最小分配空间,key+value+flags默认是48
-h 显示帮助

连接memcached服务器:telnet 192.168.1.120 11211
输入stats查看基本信息:
STAT pid 1712
STAT uptime 5247
STAT time 1402624936
STAT version 1.2.6
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 3
STAT total_connections 5
STAT connection_structures 4
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT evictions 0
STAT bytes_read 221
STAT bytes_written 512
STAT limit_maxbytes 67108864
STAT threads 1
⑤.停止memcached服务: 输入E:\memcached\memcached.exe -d stop|shutdown 进行关闭。

九、配置Session共享
①.这里需要使用一个开源项目,即Memcached Session Manager(MSM),如下链接是该开源项目的官网:
http://code.google.com/p/memcached-session-manager/
官方给出4种序列化方案,其中kryo是效率最高的,我下载的是javolution序列化方案jar包
javolution-5.4.3.1.jar
memcached-session-manager-1.6.1.jar
memcached-session-manager-tc7-1.6.1.jar #Tomcat6 则用memcached-session-manager-tc6-1.5.1.jar
msm-javolution-serializer-1.6.1.jar
msm-javolution-serializer-cglib-1.3.0.jar
msm-javolution-serializer-jodatime-1.3.0.jar
spymemcached-2.7.3.jar

②.将上面所述的MSG的jar包拷贝至Tomcat安装目录lib文件夹中
编辑Tomcat E:\JavaEE\Apache-Tomcat-7.0.41\conf\context.xml配置文件
标签之间加入如下代码:


注意此处:memcachedNodes=”n1:localhost:11211” #将localhost换成实际启动了memcached服务的主机IP

③.测试项目:http://127.0.0.1/Session/login.jsp 登录成功页面返回结果如下:

192.168.126.1 服务器处理结果 SESSIONID = 2C33E37DD3EC70F1D1D66BA31D45970A-n1
当前登录用户:1001——->>tom—–>>123456

刷新页面返回结果如下:
192.168.126.15 服务器处理结果 SESSIONID = 2C33E37DD3EC70F1D1D66BA31D45970A-n1
当前登录用户:1001——->>tom—–>>123456

处理请求的服务器IP变化了,SESSION_ID保持一致,并且SESSION中保存的用户信息未失效说明SESSION共享成功。

到此利用Nginx + Tomcat + Memcached 搭建WEB服务器负载均衡SESSION失效转移成功。