第九周作业

  1. nginx实现全栈SSL。要求http rewrite到https协议。

  2. nginx实现动静分离。

  3. nginx实现防盗链功能。

  4. 解析nginx常见的负载均衡算法。

  5. 基于LNMP完成搭建任意一种应用。

  6. jumpserver 总结安装部署,添加用户授权,行为审计。

  7. JVM垃圾回收原理,JVM调优。

  8. tomcat实现java应用发布。

  9. 实现tomcat session粘性,并验证过程。

  10. 实现tomcat会话复制集群。

一、nginx实现全栈SSL。要求http rewrite到https协议

1、配置nginx https

 server {
        listen               80;
        listen               443 ssl;
        root                 /var/www/;
        server_name          localhost;
        ssl_certificate      证书文件.crt;
        ssl_certificate_key  证书文件.key;

        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }

2、开启https

killall nginx 
nginx

3、nginx强制http跳转到https的配置文件

#先监听80端口,为http请求,然后强制转发到https监听的443端口上面
server {
  listen       80;
  root         path;
  server_name  www.exp.com;
  rewrite ^(.*) https://$server_name$1 permanent;
}

#监听443端口,https请求
server {
  listen                 443 ssl;
  root                   path;
  server_name            www.exp.com;
  ssl_certificate        证书文件.crt;
  ssl_certificate_key    证书文件.key;
  
  #此段代码为rapidPHP单一入库配置,不是rapidPHP框架请忽略
  #location / {
  #  if (!-e $request_filename){
  #    rewrite ^(.*)$ /index.php?__ROUTE__=$1 last;
  #  }
  #}

  location ~ \.php$ {
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    include        fastcgi_params;
  }
}

配置完成重启nginx即可

二、nginx实现动静分离

1.1 动态页面与静态页面区别

  • 静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源。
  • 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变。

1.2 什么是动静分离

  • 动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
  • 动静分离简单的概括是:动态文件与静态文件的分离。
  • 伪静态:网站如果想被搜索引擎搜素到,动态页面静态技术freemarker等模版引擎技术

1.3 为什么要用动静分离

  • 在我们的软件开发中,有些请求是需要后台处理的(如:.jsp,.do等等),有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件),这些不需要经过后台处理的文件称为静态文件,否则动态文件。因此我们后台处理忽略静态文件。这会有人又说那我后台忽略静态文件不就完了吗。当然这是可以的,但是这样后台的请求次数就明显增多了。在我们对资源的响应速度有要求的时候,我们应该使用这种动静分离的策略去解决。
  • 动静分离将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。这里我们将静态资源放到nginx中,动态资源转发到tomcat服务器中。
  • 因此,动态资源转发到tomcat服务器我们就使用到了前面讲到的反向代理了。

2.1 架构分析

 

 

2.2 配置

  动静分离的原理很简单,通过location对请求url进行匹配即可,在/Users/Hao/Desktop/Test(任意目录)下创建 /static/imgs 配置如下:

###静态资源访问
server {
  listen       80;
  server_name  static.haoworld.com;
  location /static/imgs {
       root /Users/Hao/Desktop/Test;
       index  index.html index.htm;
   }
}
###动态资源访问
 server {
  listen       80;
  server_name  www.haoworld.com;
    
  location / {
    proxy_pass http://127.0.0.1:8080;
     index  index.html index.htm;
   }
}

3.动静分离与前后分离区别:

  1. 动静分离动态资源与静态资源分离,不会部署在同一台服务器上。
  2. 前后分离:网站架构模式,微服务开发基于SOA面向于服务器开发,后台和前端都采用调用接口方式。将一个项目拆分成一个控制Web(前端)和接口(后端),最终使用rpc远程调用技术。视图层和业务逻辑层拆分,中间采用RPC远程调用技术

 

三、nginx实现防盗链功能

 

一:一般的防盗链如下:

location ~* \.(gif|jpg|png|swf|flv)$ { 
  valid_referers none blocked www.jzxue.com jzxue.com ; 
  if ($invalid_referer) { 
    rewrite ^/ http://www.jzxue.com/retrun.html; 
    #return 403; 
  } 
}
第一行:gif|jpg|png|swf|flv
表示对gif、jpg、png、swf、flv后缀的文件实行防盗链
第二行: 表示对www.ingnix.com这2个来路进行判断
if{}里面内容的意思是,如果来路不是指定来思是,如果来路不是指定来路就跳转到http://www.jzxue.com/retrun.html页面,当然直接返回403也是可以的。

二:针对图片目录防止盗链

location /images/ { 
  alias /data/images/; 
  valid_referers none blocked server_names *.xok.la xok.la ; 
  if ($invalid_referer) {return 403;} 
} 
三:使用第三方模块ngx_http_accesskey_module实现Nginx防盗链
实现方法如下:
1. 下载NginxHttpAccessKeyModule模块文件:http://wiki.nginx.org/File:Nginx-accesskey-2.0.3.tar.gz;
2. 解压此文件后,找到nginx-accesskey-2.0.3下的config文件。编辑此文件:替换其中的”$HTTP_ACCESSKEY_MODULE”为”ngx_http_accesskey_module”;
3. 用一下参数重新编译nginx:

./configure --add-module=path/to/nginx-accesskey
<<pestd add
上面需要加上原有到编译参数,然后执行: make && make install

修改nginx的conf文件,添加以下几行:
location /download {
  accesskey on;
  accesskey_hashmethod md5;
  accesskey_arg "key";
  accesskey_signature "mypass$remote_addr";
}
其中:
accesskey为模块开关;
accesskey_hashmethod为加密方式MD5或者SHA-1;
accesskey_arg为url中的关键字参数;
accesskey_signature为加密值,此处为mypass和访问IP构成的字符串。
访问测试脚本download.php:

<?
$ipkey= md5("mypass".$_SERVER['REMOTE_ADDR']);
$output_add_key="<a href=http://www.jzxue.com/download/G3200507120520LM.rar?key=".$ipkey.">download_add_key</a><br />";
$output_org_url="<a href=http://www.jzxue.com/download/G3200507120520LM.rar>download_org_path</a><br />";
echo $output_add_key;
echo $output_org_url;
?>
访问第一个download_add_key链接可以正常下载,第二个链接download_org_path会返回403 Forbidden错误。

 

四、解析nginx常见的负载均衡算法

Nginx负载均衡算法
1、轮询(默认)

每个请求按时间顺序逐一分配到不同的后端服务,如果后端某台服务器死机,自动剔除故障系统,使用户访问不受影响。

例如:

upstream bakend {  
    server 192.168.0.1;    
    server 192.168.0.2;  
}
 

2、weight(轮询权值)

weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。或者仅仅为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
例如:

upstream bakend {  
    server 192.168.0.1 weight=10;  
    server 192.168.0.2 weight=10;  
}
 

3、ip_hash

每个请求按访问IP的哈希结果分配,使来自同一个IP的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的session共享问题。

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
例如:

upstream bakend {  
    ip_hash;  
    server 192.168.0.1:88;  
    server 192.168.0.2:80;  
} 
 
 

4、fair(第三方)

比 weight、ip_hash更加智能的负载均衡算法,fair算法可以根据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间 来分配请求,响应时间短的优先分配。Nginx本身不支持fair,如果需要这种调度算法,则必须安装upstream_fair模块。

按后端服务器的响应时间来分配请求,响应时间短的优先分配。
例如:

upstream backend {  
    server 192.168.0.1:88;  
    server 192.168.0.2:80;  
    fair;  
}
 

5、url_hash(第三方)

按访问的URL的哈希结果来分配请求,使每个URL定向到一台后端服务器,可以进一步提高后端缓存服务器的效率。Nginx本身不支持url_hash,如果需要这种调度算法,则必须安装Nginx的hash软件包。

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

注意:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。

例如:

upstream backend {  
    server 192.168.0.1:88;  
    server 192.168.0.2:80;  
    hash $request_uri;  
    hash_method crc32;  
}

 

五、基于LNMP完成搭建任意一种应用

LNMP平台搭建与应用
1.LNMP架构概述
LNMP就是Linux+Nginx+MySQL+PHP,Linux作为服务器的操作系统、Nginx作为web服务器、PHP作为解析动态脚本语言、MySQL即为数据库
Linux作为服务器的操作系统
Nginx作为WebServer服务器
PHP作为动态解析服务,也不只是php还有python、perl
MySQL作为后端存储数据库服务
Nginx服务本身不能处理PHP请求,那么当用户发起PHP动态请求,PHP是这样处理的
用户—>http协议—>nginx—>fastcgi协议—>php-fpm
注意fastcgi是nginx连接php-fpm之间的协议
Nginx解析php的方法
1.将PHP页面的解析请求通过代理方式转发给Apache进行处理
将PHP页面的解析请求转发给php-fpm模块

1.1.LNMP架构图

 

 

 

1.2.Nginx与Fast_CGI详细工作流程

 

 

 

1.用户通过http协议发起请求,请求会先抵达LNMP架构中的Nginx
2.Nginx会根据用户的请求进行判断,这个判断会通过定义的location完成,例如图片、文件会去找ftp服务,php会去找php-fpm
3.判断用户请求的是否是静态页面,如果是静态页面Nginx会直接处理
4.判断用户请求的是否是动态页面,如果是动态页面,Nginx会将该请求交给fastcgi协议进行下发
5.fastcgi会将请求交给php-fpm管理进程,php-fpm管理进程接收到后调用具体的工作线程wrapper,类似于工单,每当工作来临时,php-fpm就会进行分配
6.wapper线程会调用php进行解析,如果只是解析代码,PHP解析完会自动返回
7.如果有查询数据库操作,则由php连接数据库(通过用户、密码、IP)进行连接然后发起查询操作
8.最终数据由mysql--->php--->php-fpm--->fastcgi--->nginx--->http--->user

2.安装LNMP架构

2.1.安装Nginx

使用官方提供的rpm包
[root@localhost ~]# cat /etc/yum.repos.d/nginx.repo 
[nginx]
name=nginx
baseurl=http://nginx.org/packages/centos/7/$basearch
enabled=1
gpgcheck=0

yum安装
[root@localhost ~]# yum -y install nginx

启动
[root@localhost ~]# systemctl start nginx
[root@localhost ~]# systemctl enable nginx

扩展
1.仅下载rpm包不进行安装,如果想安装并下载那么就把--downloadonly去掉
[root@localhost ~]# yum -y install nginx --downloadonly --downloaddir=/soft
在使用rpm安装,可以留着等下次做实例的时候用
[root@localhost soft]# rpm -ivh nginx-1.16.1-1.el7.ngx.x86_64.rpm    

2.我们在官网系在了nginx的rpm包可能会存在依赖关系
[root@localhost ~]# yum -y localinstall nginx-1.16.1-1.el7.ngx.x86_64.rpm

2.2.安装MySQL

  • mysql有两个版本可以选择一个5.6一个是5.7我们都来做一遍
  • mysql所有功能都是修改配置文件实现的

2.2.1.安装MySQL5.7

1.下载MySQL官方扩展源
[root@localhost ~]# rpm -ivh http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql57-community-release-el7-10.noarch.rpm
也可以使用wget下载下来方便以后使用
[root@localhost ~]# wgt http://repo.mysql.com/yum/mysql-5.7-community/el/7/x86_64/mysql57-community-release-el7-10.noarch.rpm

2.安装完mysqlrpm后会在/etc/yum.repos.d中生成两个文件
root@localhost ~]# ls /etc/yum.repos.d/my*
/etc/yum.repos.d/mysql-community.repo  /etc/yum.repos.d/mysql-community-source.repo

3.安装mysql5.7
把mysql5.7包下载下来,然后方便下次使用
首先配置yum,开启缓存,不然安装好后就会自动删除
[root@localhost conf.d]# vim /etc/yum.conf
keepcache=1

[root@localhost ~]# yum install mysql-community-server --downloaddir=/root/soft/ --downloadonly
在使用localinstall安装(也可以用rpm直接装)
[root@localhost soft]# yum -y localinstall mysql-community-server-5.7.29-1.el7.x86_64.rpm

4.启动
[root@localhost soft]# systemctl start mysqld
[root@localhost soft]# systemctl enable mysqld

5.由于mysql5.7默认配置了密码,需要在日志中过滤temporary password获取默认密码
[root@localhost soft]# grep  "temporary password" /var/log/mysqld.log 
2020-04-10T04:01:09.027390Z 1 [Note] A temporary password is generated for root@localhost: *2cNf4-Gh:oe

6.登录
[root@localhost ~]# mysql -u root -p*2cNf4-Gh:oe
[root@localhost ~]# mysql -uroot -p$(awk '/temporary password/{print $NF}' /var/log/mysqld.log) 

7.修改密码,密码必须是8位以上包含数字大小写字母
mysql> alter user 'root'@'localhost' identified by '1234.COm';

2.2.2.安装MySQL5.6

1.下载MySQL官方扩展源
[root@localhost ~]# rpm -ivh http://repo.mysql.com/yum/mysql-5.6-community/el/7/x86_64/mysql-community-release-el7-5.noarch.rpm

2.安装完mysqlrpm后会在/etc/yum.repos.d中生成两个文件
root@localhost ~]# ls /etc/yum.repos.d/my*
/etc/yum.repos.d/mysql-community.repo /etc/yum.repos.d/mysql-community-source.repo

3.安装mysql5.6
把mysql5.6包下载下来,然后方便下次使用(有条件的可以直接yum装)
首先配置yum,开启缓存,不然安装好后就会自动删除
[root@localhost conf.d]# vim /etc/yum.conf
keepcache=1

[root@localhost ~]# yum install mysql-community-server --downloaddir=/root/soft/ --downloadonly

[root@localhost ~]# ls soft/
mysql-community-client-5.6.47-2.el7.x86_64.rpm mysql-community-libs-5.6.47-2.el7.x86_64.rpm mysql-community-common-5.6.47-2.el7.x86_64.rpm mysql-community-server-5.6.47-2.el7.x86_64.rpm

在使用localinstall安装
[root@localhost soft]# yum -y localinstall *.rpm
或者
[root@localhost soft]# yum -y localinstall mysql-community-server-5.7.29-1.el7.x86_64.rpm

如果网速不好则用rpm安装,安装顺序如下
[root@localhost soft]# rpm -ivh mysql-community-common-5.6.47-2.el7.x86_64.rpm
[root@localhost soft]# rpm -ivh mysql-community-libs-5.6.47-2.el7.x86_64.rpm
[root@localhost soft]# rpm -ivh mysql-community-client-5.6.47-2.el7.x86_64.rpm
[root@localhost soft]# rpm -ivh mysql-community-server-5.6.47-2.el7.x86_64.rpm

4.启动
[root@localhost soft]# systemctl start mysqld
[root@localhost soft]# systemctl enable mysqld

5.登录
[root@localhost ~]# mysql

6.修改密码
[root@localhost ~]# mysqladmin -u root password 123
Warning: Using a password on the command line interface can be insecure.    //这个提示表示密码太短
如果想改密码可以这样操作
[root@localhost ~]# mysqladmin -u root -p123 password 1234
上条没有没有加-p表示mysql没有密码因为是首次安装
下面这条加了-p表示指定之前的密码

7.其他服务器远程连接mysql
首先在mysql服务器执行:
mysql> grant all privileges on *.* to 'root'@'%' identified by '123' with grant option;
mysql> flush privileges;

远程主机
mysql -uroot -p123 -h 192.168.81.210

2.3.安装PHP

  • 使用第三方扩展源安装php7.1
1.删除之前系统安装的php
[root@localhost php]# yum remove php-mysql-5.4 php php-fpm php-common

2.安装扩展源
[root@localhost php]# wget http://mirror.webtatic.com/yum/el7/webtatic-release.rpm
[root@localhost php]# yum -y localinstall webtatic-release.rpm

3.安装php7.1
如果https访问不到那么久在安装之前把/etc/yum.repos.d/webtatic.repo,把里面的https改成http,例如:mirrorlist=http://mirror.webtatic.com/yum/el7/SRPMS/mirrorlist
把rpm包下载到指定目录,方便下次使用,共计25个包
首先配置yum,开启缓存,不然安装好后就会自动删除
[root@localhost conf.d]# vim /etc/yum.conf
keepcache=1

[root@localhost soft]# yum -y install php71w php71w-cli php71w-common php71w-devel php71w-embedded php71w-gd php71w-mcrypt php71w-mbstring php71w-pdo php71w-xml php71w-fpm php71w-mysqlnd php71w-opcache php71w-pecl-memcached php71w-pecl-redis php71w-pecl-mongodb --downloaddir=/root/soft/ --downloadonly
[root@localhost soft]# yum -y localinstall *.rpm

4.启动
[root@localhost soft]# systemctl start php-fpm
[root@localhost soft]# systemctl enable php-fpm
Created symlink from /etc/systemd/system/multi-user.target.wants/php-fpm.service to /usr/lib/systemd/system/php-fpm.service.

5.查看端口,到此完成
[root@localhost soft]# ss -lnptu | grep nginx
tcp    LISTEN     0      128       *:80                    *:*                   users:(("nginx",pid=7375,fd=12),("nginx",pid=7374,fd=12))
[root@localhost soft]# ss -lnptu | grep mysql
tcp    LISTEN     0      80       :::3306                 :::*                   users:(("mysqld",pid=20862,fd=10))
[root@localhost soft]# ss -lnptu | grep php
tcp    LISTEN     0      128    127.0.0.1:9000                  *:*                   users:(("php-fpm",pid=23602,fd=9),("php-fpm",pid=23601,fd=9),("php-fpm",pid=23600,fd=9),("php-fpm",pid=23599,fd=9),("php-fpm",pid=23598,fd=9),("php-fpm",pid=23595,fd=7))


没有网络的情况下,把所有的PHP的rpm包拷到服务器,执行php_install.sh即可

3.验证LNMP是否可用

3.1.Nginx解析php页面

  • 验证nginx是否能正常解析php动态请求,以及php程序是否正常连接数据库
  • 小技巧:location /{}中配置内容可以直接写在server中
1.配置server
[root@localhost conf.d]# vim php.conf
server {
        listen 80;
        server_name phptest.com;
        root /web/phptest;
        index index.php index.html;

        location ~ \.php$ {
                root /web/phptest;
                fastcgi_pass 127.0.0.1:9000;            //fastcgi调用php-fpm
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;        //$document_root表示/etc/nginx也就是nginx安装目录
                include fastcgi_params;                //引入fastcgi脚本,这个include表示上级目录也可以写绝对路径

        }
}

2.写入php页面
[root@localhost conf.d]# vim /web/phptest/index.php
<?php
    phpinfo();
?>

3.重载nginx
[root@localhost conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@localhost conf.d]# systemctl reload nginx

 

 

3.2.PHP连接MySQL

直接在站点目录下面写一个php文件即可,不需要重载
[root@bogon phptest]# vim mysqltest.php
<?php 
    $servername = "localhost";
    $username = "root";
    $password = "123";
    
    //创建链接
    $conn = mysqli_connect($servername, $username, $password);

    //检测连接
    if (!$conn) {
        die("Connection failed: " . mysqli_connect_error());
    }
    echo "连接成功"

?>

 

 

3.3.扩展方式加主机头访问

  • 不写全网站根目录即可
server {
        listen 80;
        server_name php.com;

        location / {
                root /web;
                index index.php;
        }

        location ~ \.php$ {
                root /web;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
        }
}

4.部署WordPress博客站点

4.1.配置WordPress站点

[root@localhost conf.d]# vim wordpress.conf
#wordpress
server {
        listen 80;
        server_name jiangxl.wordpress.com;

        location / {
                root /web/wordpress;
                index index.php index.html;
        }

        location ~ \.php$ {
                root /web/wordpress;
                fastcgi_pass 127.0.0.1:9000;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILRNAME $document_root$fastcgi_script_name;
                include /etc/nginx/fastcgi_params;
        }
}

[root@localhost conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@localhost conf.d]# systemctl reload nginx

4.2.部署WordPress源码

wget https://cn.wordpress.org/wordpress-4.9.4-zh——CN.tar.gz

由于网站登录失败,因此我从第三方下载了wordpress-5.2.3-zh_CN.zip,上传到/web目录
解包
[root@bogon web]# mkdir /web
[root@bogon web]# cd /web
[root@bogon web]# unzip wordpress-5.2.3-zh_CN.zip

到此WordPress站点可以访问到安装界面

 

 

4.3.配置MySQL

  • wordpress会用到后台数据库,因为博客肯定是需要上传文章附件等等,不配置数据库页面显示如下

 

 需要手动创建数据库,开始配置mysql

[root@localhost conf.d]# mysql -uroot -p123
mysql> create database wordpress;
Query OK, 1 row affected (0.00 sec)

mysql> exit

4.4.配置网站属主

  • 默认nginx的所属用户时nginx,nginx用户对/web/wordpress没有写入权限,因此会无法生成wp-config.php文件,因此我们需要修改网站属主

 

 

  • nginx也要修改程序用户,让通过nginx访问页面的用户具有写权限
  • 同样php-fpm也要修改程序用户,让通过访问php页面的用户具有写权限
  • 小技巧,搞架构记得程序所属用户设置成一样的,开始配置
[root@localhost conf.d]# groupadd -g 888 www
[root@localhost conf.d]# useradd -u 888 -g 888 www
[root@localhost conf.d]# chown -R www.www /web/wordpress

修改nginx所属用户
[root@localhost conf.d]# sed -ri '/^user/c user www;' ../nginx.conf
重载让日志生效
[root@localhost conf.d]# systemctl reload nginx

修改php-fpm所属用户
www.conf是他的模板文件,主配置文件是php-fpm.conf、php.ini
[root@localhost conf.d]# sed -ri '/^user/c user = www' /etc/php-fpm.d/www.conf
[root@localhost conf.d]# sed -ri '/^group/c group = www' /etc/php-fpm.d/www.conf
重启让日志生效
[root@localhost conf.d]# systemctl restart php-fpm

4.5.页面安装

4.5.1.开始安装

 

 

4.5.2.填写数据库信息

 

 

4.5.3.确认安装

  • 这一步就生成了wp-config.php文件

 

 

 

 

4.5.4.配置后台

4.5.5.安装完成

 

 

5.部署wecenter知乎系统

5.1.配置wecenter站点

[root@localhost conf.d]# vim wecenter.conf
#wecenter
server {
        listen 80;
        server_name jxl.wecenter.com;
        root /web/wecenter;
        index index.php index.html;

        location ~ \.php$ {
        root /web/wecenter;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        }

}

[root@localhost conf.d]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@localhost conf.d]# systemctl reload nginx

5.2.部署wecenter源码

[root@jxl web]# unzip -d wecenter WeCenter_3-3-4.zip
[root@jxl web]# chown -R www:www /web/

5.3.配置数据库

创建wecenter数据库就行
[root@localhost web]# mysql -uroot -p123
mysql> create database wecenter;
Query OK, 1 row affected (0.00 sec)

mysql> exit
Bye

5.4.页面安装

5.4.1.服务器环境检测

 

 

6.4.2.配置数据库

 

 

6.4.3.添加后台管理员

 

 

6.4.4.安装完成

 

 

6.4.5.网站首页

 

 

6.4.6.登录后台

 

 

 

 

六、jumpserver 总结安装部署,添加用户授权,行为审计

一、介绍

Jumpserver 是全球首款完全开源的堡垒机, 使用 GNU GPL v2.0 开源协议, 是符合 4A 的专业运维审计系统。

Jumpserver 使用 Python / Django 进行开发, 遵循 Web 2.0 规范, 配备了业界领先的 Web Terminal 解决方案, 交互界面美观、用户体验好。

Jumpserver 采纳分布式架构, 支持多机房跨区域部署, 中心节点提供 API, 各机房部署登录节点, 可横向扩展、无并发访问限制。

Jumpserver 现已支持管理 SSH、 Telnet、 RDP、 VNC 协议资产。

 

二、主要功能

用户管理:用户管理模块,负责添加修改删除用户,把用户划分不同的用户组,方便将来授权主机.
资产管理:资产管理模块,负责管理各类资产,采纳资产数方便组织和授权.
授权管理:授权管理模块,以资产树方式授权资产,效率高
日志审计:日志审计模块,监控用户操作,统计用户操作记录,可中断用户不良危险操作.
三、安装方法

1、环境准备

CPU: 64位双核处理器
内存: 4G DDR3
数据库:mysql 版本大于等于 5.6 mariadb 版本大于等于 5.5.6
系统: CentOS 7
IP:192.168.100.144
2、设置防火墙和Selinux

$ firewall-cmd --zone=public --add-port=80/tcp --permanent  # nginx 端口
$ firewall-cmd --zone=public --add-port=2222/tcp --permanent  # 用户SSH登录端口 coco
 
$ firewall-cmd --reload  # 重新载入规则
 
$ setenforce 0
$ sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
 
# 修改字符集, 否则可能报 input/output error的问题, 因为日志里打印了中文
$ localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8
$ export LC_ALL=zh_CN.UTF-8
$ echo 'LANG="zh_CN.UTF-8"' > /etc/locale.conf
3、准备python3和python虚拟环境
3.1、安装依赖包
$ yum -y install wget gcc epel-release git
 
3.2、安装Python3.6
$ yum -y install python36 python36-devel
 
# 如果下载速度很慢, 可以换国内源
$ wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
$ yum -y install python36 python36-devel
 
3.3 建立python虚拟环境
$ cd /opt
$ python3.6 -m venv py3
$ source /opt/py3/bin/activate
 
# 看到下面的提示符代表成功, 以后运行 Jumpserver 都要先运行以上 source 命令, 以下所有命令均在该虚拟环境中运行
(py3) [root@localhost py3]
4、安装Jumpserver

4.1、项目提交较多 git clone 时较大, 你可以选择去 Github 项目页面直接下载zip包。
 
$ cd /opt/
$ git clone --depth=1 https://github.com/jumpserver/jumpserver.git
 
4.2、安装依赖RPM包
$ cd /opt/jumpserver/requirements
$ yum -y install $(cat rpm_requirements.txt)  # 如果没有任何报错请继续
 
4.3、安装Python库依赖
$ yum instatll python-pip
$ pip install --upgrade pip setuptools
$ pip install -r requirements.txt
 
# 如果下载速度很慢, 可以换国内源
$ pip install --upgrade pip setuptools -i https://mirrors.aliyun.com/pypi/simple/
$ pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
5、安装Redis,用来做cache和celery broke
$ yum -y install redis
$ systemctl enable redis
$ systemctl start redis
6、安装Mysql,创建Jumpserver数据库
$ yum -y install mariadb mariadb-devel mariadb-server # centos7下安装的是mariadb
$ systemctl enable mariadb
$ systemctl start mariadb
 
创建Jumpserver数据库并授权
$ DB_PASSWORD=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 24`  # 生成随机数据库密码
$ echo -e "\033[31m 你的数据库密码是 $DB_PASSWORD \033[0m"
 你的数据库密码是 1uqXf7Ve79gDTdUEFhURgKLe
 
$ mysql -uroot -e "create database jumpserver default charset 'utf8'; grant all on jumpserver.* to 'jumpserver'@'127.0.0.1' identified by '$DB_PASSWORD'; flush privileges;"
7、修改Jumpserver的配置文件 
$ cd /opt/jumpserver
$ cp config_example.yml config.yml
 
$ SECRET_KEY=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50`  # 生成随机SECRET_KEY
$ echo "SECRET_KEY=$SECRET_KEY" >> ~/.bashrc
$ BOOTSTRAP_TOKEN=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 16`  # 生成随机BOOTSTRAP_TOKEN
$ echo "BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN" >> ~/.bashrc
 
$ sed -i "s/SECRET_KEY:/SECRET_KEY: $SECRET_KEY/g" /opt/jumpserver/config.yml
$ sed -i "s/BOOTSTRAP_TOKEN:/BOOTSTRAP_TOKEN: $BOOTSTRAP_TOKEN/g" /opt/jumpserver/config.yml
$ sed -i "s/# DEBUG: true/DEBUG: false/g" /opt/jumpserver/config.yml
$ sed -i "s/# LOG_LEVEL: DEBUG/LOG_LEVEL: ERROR/g" /opt/jumpserver/config.yml
$ sed -i "s/# SESSION_EXPIRE_AT_BROWSER_CLOSE: false/SESSION_EXPIRE_AT_BROWSER_CLOSE: true/g" /opt/jumpserver/config.yml
$ sed -i "s/DB_PASSWORD: /DB_PASSWORD: $DB_PASSWORD/g" /opt/jumpserver/config.yml
 
$ echo -e "\033[31m 你的SECRET_KEY是 $SECRET_KEY \033[0m" 你的SECRET_KEY是 dkQB4KN48GSy1vPv0vQZq5PIghGkewE169IliSALjLxO0dosUl
$ echo -e "\033[31m 你的BOOTSTRAP_TOKEN是 $BOOTSTRAP_TOKEN \033[0m"
 你的BOOTSTRAP_TOKEN是 ElwLcfEzFoeMpy1F
 
 
$ vi config.yml  # 确认内容有没有错误
 
# SECURITY WARNING: keep the secret key used in production secret!
# 加密秘钥 生产环境中请修改为随机字符串, 请勿外泄
SECRET_KEY:
 
# SECURITY WARNING: keep the bootstrap token used in production secret!
# 预共享Token coco和guacamole用来注册服务账号, 不在使用原来的注册接受机制
BOOTSTRAP_TOKEN:
 
# Development env open this, when error occur display the full process track, Production disable it
# DEBUG 模式 开启DEBUG后遇到错误时可以看到更多日志
DEBUG: false
 
# DEBUG, INFO, WARNING, ERROR, CRITICAL can set. See https://docs.djangoproject.com/en/1.10/topics/logging/
# 日志级别
LOG_LEVEL: ERROR
# LOG_DIR:
 
# Session expiration setting, Default 24 hour, Also set expired on on browser close
# 浏览器Session过期时间, 默认24小时, 也可以设置浏览器关闭则过期
# SESSION_COOKIE_AGE: 86400
SESSION_EXPIRE_AT_BROWSER_CLOSE: true
 
# Database setting, Support sqlite3, mysql, postgres ....
# 数据库设置
# See https://docs.djangoproject.com/en/1.10/ref/settings/#databases
 
# SQLite setting:
# 使用单文件sqlite数据库
# DB_ENGINE: sqlite3
# DB_NAME:
 
# MySQL or postgres setting like:
# 使用Mysql作为数据库
DB_ENGINE: mysql
DB_HOST: 127.0.0.1
DB_PORT: 3306
DB_USER: jumpserver
DB_PASSWORD:
DB_NAME: jumpserver
 
# When Django start it will bind this host and port
# ./manage.py runserver 127.0.0.1:8080
# 运行时绑定端口
HTTP_BIND_HOST: 0.0.0.0
HTTP_LISTEN_PORT: 8080
 
# Use Redis as broker for celery and web socket
# Redis配置
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
# REDIS_PASSWORD:
# REDIS_DB_CELERY: 3
# REDIS_DB_CACHE: 4
 
# Use OpenID authorization
# 使用OpenID 来进行认证设置
# BASE_SITE_URL: http://localhost:8080
# AUTH_OPENID: false  # True or False
# AUTH_OPENID_SERVER_URL: https://openid-auth-server.com/
# AUTH_OPENID_REALM_NAME: realm-name
# AUTH_OPENID_CLIENT_ID: client-id
# AUTH_OPENID_CLIENT_SECRET: client-secret
 
# OTP settings
# OTP/MFA 配置
# OTP_VALID_WINDOW: 0
# OTP_ISSUER_NAME: Jumpserver
8、运行Jumpserver
$ cd /opt/jumpserver
$ ./jms start all -d  # 后台运行使用 -d 参数./jms start all -d
 
# 新版本更新了运行脚本, 使用方式./jms start|stop|status all  后台运行请添加 -d 参数

 

七、JVM垃圾回收原理,JVM调优

对JVM有了一定的认识以后,我们自然而然的就会知道两个异常,一个叫栈溢出(StackOverFlowError),一个叫堆溢出(OutOfMemory)或者说是内存泄漏;在Java内存模型中,栈是每一个线程私有的,随着线程的创建而创建,当栈的内存不足时,就会导致栈溢出的情况;而堆溢出则是指JVM在无法为对象申请到足够的内存空间时而抛出的异常;当我们的程序中出现这两个问题时,此时我们就需要对JVM进行一个调优处理。

调优的核心参数
在Java8以后,内存模型中的永久代已经被移除,取而代之的是元空间;永久代和元空间最大的不同在于前者使用的是JVM的堆内存,但是后者却已经不在JVM中了,它使用的是物理内存,因此在Java8以后,关于JVM的调优我们只需要关注堆中的新生区和养老区,如下图所示

 

 

 

常见配置汇总


垃圾回收机制
当类加载器把新的对象放到堆中时会触发JVM的GC,但JVM在进行GC时,并非每次都对堆内存中的三个区域一起回收的,大部分时候的回收其实都只是针对新生代,因此GC按照回收区域划分可以分为两种,一种是普通GC(Minor GC),一种是全局GC(Full GC);普通GC只针对新生代区域进行垃圾收集操作,因为大多数的Java对象存活率不高,因此普通GC非常频繁,速度也较快;全局GC只针对老年代进行垃圾收集操作,因为老年区的内存空间较大,所以全局GC相对于普通GC的频率较低,速度慢10以上,且全局GC一旦触发,也就意味着系统要考虑内存优化的问题了;JVM垃圾回收机制大致如下图所示

 

 

 

四种垃圾回收算法简介
引用计数法

 

 



复制算法
应用在年轻代中的GC算法,该算法的基本思想就是将内存分成两块,每次只用其中一块,当这一块内存用完,就会进行GC,未被回收的对象则复制到另一块区域,依次类推,大致原理如下

 

 

 

标记清除法
用通俗的话来说,标记清除算法就是程序运行期间,当内存快要被耗尽时,GC就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理共作然后让程序恢复运行

 

 

 

标记压缩法
该算法与上标记清除法唯一的区别就是在它的基础上增加了一个压缩的操作,这样就能解决内存碎片的问题,但是却带来来效率不高的问题,其原理大致如下图所示:

 

 

 

总结
对于上述四种算法,单从效率上来说,复制算法无疑是最快的,但是却浪费来较多的内存,标记清除和标记整理方法虽然兼顾了内存碎片这一个问题,但其效率较慢,所以我们无法从绝对意义上说这四种算法谁优谁劣。只是在实际应用中,我们发现复制算法在新生区效果更好一些,而标记清除或者标记整理在养老区则更强劲,因此我们把JVM的垃圾回收称作分代收集算法。

 

八、tomcat实现java应用发布

客户端安装xshell访问云端Linux

https://www.netsarang.com/download/down_form.html?code=622
(选择家庭学校版本,除邮箱以外,其他空行任意填)

Linx安装java环境

安装默认版本 yum install java
安装指定版本 yum install java-1.8.0-openjdk* -y

 

 

 

 

 

选择 y 即可开始下载

Linx安装tomcat yum install tomcat

启动tomcat ./startup.sh
相关启动命令:启动,重启,停止
systemctl start tomcat
systemctl restart tomcat
systemctl stop tomcat

查看tomcat状态 ps -aux | grep tomcat

 

 

 

上图表示tomcat在运行状态

windows上传war包到webapp目录下
可以用支持sftp的软件上传,如FlashFXP、FileZilla

 

 

 

也可以访问 localhost:8080/manager/ (默认账号和密码都是 tomcat)

 

 

 

 

Linux环境下上传war,直接使用scp命令

查看项目是否发布成功
curl http://localhost:8080
得到请求页面(即欢迎页面)的源码,说明搭建成功

 

 

 

 


输入ip:8080/war包名称/ 即可进入发布的项目的首页

 

 

 



 

九、实现tomcat session粘性,并验证过程

方案:
1、基于request的负载均衡
    该种方式下,负载均衡器 (load balancer)会根据各个node的状况,把每个 http request进行分发。使用这样的均衡策略,就必须在多个node之间复制用户的session,实时保持整个cluster的用户状态同步,这种操作被称为session复制(session replication)。Jboss的实现原理是使用拦截器(interceptor),根据用户的同步策略拦截request,做同步处理后再交给server产生响应。

     优点是客户不会被绑定都具体的node,只要还有一个node存活,用户状态都不会丢失,cluster都能够继续工作。

缺点是node之间通信频繁,响应速度有影响,多并发、高频操作的情况下性能下降比较厉害。

2、    基于用户的负载均衡
该种方式下,当用户发出第一个request后,负载均衡器动态的把该用户分配到某个节点,并记录该节点的jvm路由,以后该用户的所有request都会被绑定这个jvm路由,用户只会与该server发生交互,这种策略被称为粘性session(session sticky)。

     优点是响应速度快,多个节点之间无须通信。

缺点也很明显,某个node死掉以后,它负责的所有用户都会丢失session。

 

采取方案:

         目前采用第二种,也就是session stiky (1小时)

         文件服务器的配置   (1天)  

         Tomcat参数优化    (1天)

集群的配置

 

首先在apache 的conf目录下找到httpd.conf(apache 的配置文件)文件

在该文件里找到

LoadModule proxy_module modules/mod_proxy.so

LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

LoadModule proxy_balancer_module modules/mod_proxy_balancer.so

LoadModule proxy_connect_module modules/mod_proxy_connect.so

LoadModule proxy_ftp_module modules/mod_proxy_ftp.so

LoadModule proxy_http_module modules/mod_proxy_http.so

并且把上面的#号去掉

然后找到

Include conf/extra/httpd-vhosts.conf

把上面的#号去掉

在最后一行加上

ProxyRequests Off

 

<proxy balancer://cluster>

 

BalancerMember ajp://127.0.0.1:8010 loadfactor=1 route=tomcat3(与该tomcat里德jvmRote名字一样)

 

BalancerMember ajp://127.0.0.1:8020 loadfactor=1 route=tomcat2

 

</proxy>

 

第二步找到\conf\extra包下面的httpd-vhosts.conf

在下面加上

<VirtualHost *:80>

ServerAdmin yancheng100088@163.com

ServerName localhost

ServerAlias   localhost

ProxyPass / balancer://cluster/ stickysession=jsessionid nofailover=On

ProxyPassReverse / balancer://cluster/

ErrorLog "logs/dummy-host2.163.com-error.log"

    CustomLog "logs/dummy-host2.163.com-access.log" common

</VirtualHost>

 

 

第三步找到解压后的tomcat在不同的tomcat里修改server.xml里的端口参数使之不重复

(大家都知道就不详细说了)

这里改几个比较重要的参数

一个是<Engine name="Catalina" defaultHost="localhost">

改成<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat2">

另一个后面改成tomcat3

 

然后加上

<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"

                                                        mcastBindAddress="127.0.0.1"

                        address="228.0.0.4"

                        port="45564"

                        frequency="500"

                        dropTime="3000"/>

            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"

                                               tcpListenAddress="127.0.0.1"

                      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.MessageDispatch15Interceptor"/>

          </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.JvmRouteSessionIDBinderListener"/>

          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>

        </Cluster>      

这个是session复制用的不需要session复制的可以不用

 

最后一步

在需要session复制的程序里面的web.xml里加上

 

  <distributable />

 

 

 

Session粘性,一般用到粘性就不用sesssion复制了

只需要在httpd.conf里最后一段加上

ProxyRequests Off

ProxyPass / balancer://cluster/ lbmethod=byrequests stickysession=JSESSIONID nofailover=Off timeout=5 maxattempts=3 

ProxyPassReverse / balancer://cluster/ 

【www.sufms.com】

<proxy balancer://cluster>

 

BalancerMember ajp://127.0.0.1:8010 loadfactor=1 route=tomcat3

 

BalancerMember ajp://127.0.0.1:8020 loadfactor=1 route=tomcat2

 

</proxy>

就行了。

  

十、实现tomcat会话复制集群

一.tomcat会话管理
1.声明
对于WEB应用集群的技术实现而言,最大的难点就是如何能在集群中的多个节点之间保持数据的一致性,会话(Session)信息是这些数据中最重要的一块。要实现这一点,大体上有两种方式,一种是把所有Session数据放到一台服务器上或者数据库中,集群中的所有节点通过访问这台Session服务器来获取数据;另一种就是在集群中的所有节点间进行Session数据的同步拷贝,任何一个节点均保存了所有的Session数据。
两种方式都各有优点,第一种方式简单、易于实现,但是存在着Session服务器发生故障会导致全系统不能正常工作的风险;第二种方式可靠性更高,任一节点的故障不会对整个系统对客户访问的响应产生影响,但是技术实现上更复杂一些。
tomcat提供了这两种session共享的支持,但是tomcat一般采用第二种;


2.session管理机制
会话管理器定义:Session管理器组件负责管理Session对象,例如,创建和销毁Session对象。

(1)Manager:定义了关联到某一个容器的用来管理session池的基本接口。

(2)ManagerBase:实现了Manager接口,该类提供了Session管理器的常见功能的实现。


(3)StandardManager:继承自ManagerBase,tomcat的默认Session管理器(不指定配置,默认使用这个),是tomcat处理session的非集群实现(也就说是单机版的),tomcat关闭时,内存session信息会持久化到磁盘保存为SESSION.ser,再次启动时恢复。


(4)PersistentManagerBase:继承自ManagerBase,实现了和定义了session管理器持久化的基础功能。


(5)PersistentManager:继承自PersistentManagerBase,主要实现的功能是会把空闲的会话对象(通过设定超时时间)交换到磁盘上。


(6)ClusterManager:实现了Manager接口,通过类名应该能猜到,这个就是管理集群session的管理器和上面那个StandardManager单机版的session管理器是相对的概念。这个类定义类集群间session的复制共享接口。


(7)ClusterManagerBase:实现了ClusterManager接口,继承自ManagerBase。该类实现了session复制的基本操作。


(8)BackupManager:继承自ClusterManagerBase,集群间session复制策略的一种实现,会话数据只有一个备份节点,这个备份节点的位置集群中所有节点都可见。这种设计使它有个优势就是支持异构部署。


(9)DeltaManager:继承自ClusterManagerBase,集群建session复制策略的一种实现,和BackupManager不同的是,会话数据会复制到集群中所有的成员节点,这也就要求集群中所有节点必须同构,必须部署相同的应用。

 


3.Manager
会话管理组件Manger对象用于实现HTTP会话管理的功能,其有5种Manger的实现:

(1)标准会话管理器StandardManager:
Tomcat7的默认会话管理器,用于非集群环境中对单个处于运行状态的Tomcat实例会话进行管理。当Tomcat关闭时,这些会话相关的数据会被写入磁盘上的一个名叫SESSION.ser的文件中,并在Tomcat下次启动时读取此文件。
    默认只有在Tomcat正常关闭时才会保存完整的用户会话信息,异常关闭时则不会保存下来;

    默认保存于$CATALINA_HOME/work/Catalina///下的SESSIONS.ser文件中;

    若是自定义的虚拟主机则保存在$CATALINA_HOME/work/Catalina//_/ 下的 SESSIONS.ser 文件中;
定义:

  1. <Manager className="org.apache.catalina.session.StandardManager" maxInactiveInterval="7200"/>

可用属性:
       

 maxActiveSessions:最多允许的活动会话数量,默认为-1,表示不限制; 
        maxInactiveInterval:非活动的会话超时时长,默认为60s;
        pathname:会话文件的保存目录;

 



(2).持久会话管理器PersistentManager
将会话数据保存至持久存储中,并且能在服务器意外中止后重新启动时重新加载这些会话信息。持久会话管理器支持将会话保存至文件存储(FileStore)或JDBC存储(JDBCStore)中。当一个会话长时间处于空闲状态(超过设定的时间)时会被写入到swap会话对象,这对于内存资源比较吃紧的应用环境来说比较有用。和标准会话管理器一样,都是用在tomcat单实例的会话管理器;

保存至文件中的示例:

<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
         <Store className="org.apache.catalina.session.FileStore" directory="/session/tomcat"/>
        </Manager>

         每个用户的会话会被保存至directory指定的目录中的文件中,文件名为.session,并通过后台线程每隔一段时间(checkInterval参数定义,默认为60秒)检查一次超时会话;

保存至JDBCStore中的示例:

<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true">
         <Store className="org.apache.catalina.session.JDBCStore" driverName="com.mysql.jdbc.Driver"
         connectionURL="jdbc:mysql://localhost:3306/mydb?user=jb;password=pw"/>
        </Manager>

 

补充:
PersistentManagerBase类中有个成员变量Store,持久化session管理器的存储策略就是有这个Store对象定义的,这个Store的类继承结构如下:

简述:接口Store及其实例是为session管理器提供了一套存储策略,store定义了基本的接口,而StoreBase提供了基本的实现。其中FileStore类实现的策略是将session存储在以setDirectory()指定目录并以.session结尾的文件中的。JDBCStore类是将Session通过JDBC存入数据库中,因此需要使用JDBCStore,需要分别调用setDriverName()方法和setConnectionURL()方法来设置驱动程序名称和连接URL。


(3)DeltaManager
用于Tomcat集群(复制集群)的会话管理器,它通过将改变了的会话数据同步给集群中的其它节点以实现会话复制。这种实现会将所有会话的改变同步给集群中的每一个节点,也是在集群环境中用得最多的一种实现方式。这个管理器仅适用于小的架构模型,因为太多的节点间会话复制会消耗很多的系统资源,较大的一般都使用session服务器;


(4)BackupManager
用于Tomcat集群的会话管理器,与DeltaManager不同的是,某节点会话的改变只会同步给集群中的另一个而非所有节点;


5)SimpleTcpReplicationManager
最初设计该集群会话管理器是为了Tomcat 4和最早的实现,Tomcat委员会建议使用DeltaManager替代这一管理器;


二.配置tomcat的session复制集群
接着上一次的负载均衡继续配置(mod_proxy):http://blog.chinaunix.net/uid-30212356-id-5718697.html
配置环境:
前端:192.168.85.128+Apache
后端1:192.168.85.129+tomcat
后端2:192.168.85.130+tomcat

上一次虽然做了tomcat的负载均衡,保证了用户的请求能始终在同一台tomcat上,但是用户的会话还是在时刻变着的,接下来要做的就是要保证用户会话(sessionid)保持不变;
负载均衡时:

刷新页面:


但是在基于负载均衡的同时加上会话保持(stickysession=JSESSIONID)时,用户会话的sessionid就不会变!
负载均衡+会话保持时:

刷新页面:

1.跟集群相关的组件(摘自马哥笔记)
Stores:PersistentManager必须包含一个Store元素以指定将会话数据存储至何处。这通常有两种实现方式:FileStore和JDBCStore。


Resources:经常用于实现在Context中指定需要装载的但不在Tomcat本地磁盘上的应用资源如Java类,HTML页面,JSP文件等。


Cluster:专用于配置Tomcat集群的元素,可用于Engine和Host容器中。在用于Engine容器中时,Engine中的所有Host均支持集群功能。在Cluster元素中,需要直接定义一个Manager元素,这个Manager元素有一个其值为org.apache.catalina.ha.session.DeltaManager或org.apache.catalina.ha.session.BackupManager的className属性。同时,Cluster中还需要分别定义一个Channel和ClusterListener元素。


Channel:用于Cluster中给集群中同一组中的节点定义通信“信道”。Channel中需要至少定义Membership、Receiver和Sender三个元素,此外还有一个可选元素Interceptor。


Membership:用于Channel中配置同一通信信道上节点集群组中的成员情况,即监控加入当前集群组中的节点并在各节点间传递心跳信息,而且可以在接收不到某成员的心跳信息时将其从集群节点中移除。Tomcat6中Membership的实现是org.apache.catalina.tribes.membership.McastService。


Sender:用于Channel中配置“复制信息”的发送器,实现发送需要同步给其它节点的数据至集群中的其它节点。发送器不需要属性的定义,但可以在其内部定义一个Transport元素。


Transport:用于Sender内部,配置数据如何发送至集群中的其它节点。Tomcat6有两种Transport的实现:
        1) PooledMultiSender
        基于Java阻塞式IO,可以将一次将多个信息并发发送至其它节点,但一次只能传送给一个节点。

        2)PooledParallelSener
        基于Java非阻塞式IO,即NIO,可以一次发送多个信息至一个或多个节点。


Receiver:用于Channel定义某节点如何从其它节点的Sender接收复制数据,Tomcat6中实现的接收方式有两种BioReceiver和NioReceiver。


2.默认集群配置(此部分摘自网络,仅供参考)
tomcat7默认的集群配置如下,其配置细节实际上被忽略了

  

 

<!--
    <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
-->

 

而完整的默认配置如下(对于大多数应用而言,使用默认配置已经足够):

<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="228.0.0.4"
                        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.MessageDispatch15Interceptor"/>
          </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.JvmRouteSessionIDBinderListener"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>

 

解释说明:
tomcat集群各节点通过建立tcp链接来完成Session的拷贝,拷贝有同步和异步两种模式。
在同步模式下,对客户端的响应必须在Session拷贝到其他节点完成后进行;异步模式无需等待Session拷贝完成就可响应。异步模式更高效,但是同步模式可靠性更高。
同步异步模式由channelSendOptions参数控制,默认值是8,为异步模式,4是同步模式。在异步模式下,可以通过加上拷贝确认(Acknowledge)来提高可靠性,此时channelSendOptions设为10。
 
Manager用来在节点间拷贝Session,默认使用DeltaManager,DeltaManager采用的一种all-to-all的工作方式,即集群中的节点会把Session数据向所有其他节点拷贝,而不管其他节点是否部署了当前应用。
当集群中的节点数量很多并且部署着不同应用时,可以使用BackupManager,BackManager仅向部署了当前应用的节点拷贝Session。但是到目前为止BackupManager并未经过大规模测试,可靠性不及DeltaManager。
 
Channel负责对tomcat集群的IO层进行配置。


Membership用于发现集群中的其他节点,这里的address用的是组播地址(Multicastaddress,使用同一个组播地址和端口的多个节点同属一个子集群,因此通过自定义组播地址和端口就可将一个大的tomcat集群分成多个子集群。


Receiver用于各个节点接收其他节点发送的数据,在默认配置下tomcat会从4000-4100间依次选取一个可用的端口进行接收,自定义配置时,如果多个tomcat节点在一台物理服务器上,注意要使用不同的端口。


Sender用于向其他节点发送数据,具体实现通过Transport配置,PooledParallelSender是从tcp连接池中获取连接,可以实现并行发送,即集群中的多个节点可以同时向其他所有节点发送数据而互不影响。


Interceptor有点类似下面将要解释的Valve,起到一个阀门的作用,在数据到达目的节点前进行检测或其他操作,如TcpFailureDetector用于检测在数据的传输过程中是否发生了tcp错误。关于Channel的编程模型,请参考
 
Valve用于在节点向客户端响应前进行检测或进行某些操作,ReplicationValve就是用于用于检测当前的响应是否涉及Session数据的更新,如果是则启动Session拷贝操作,filter用于过滤请求,如客户端对图片,css,js的请求就不会涉及Session,因此不需检测,默认状态下不进行过滤,监测所有的响应。


JvmRouteBinderValve会在前端Apache的mod_jk发生错误时保证同一客户端的请求发送到集群的同一个节点,tomcat官方文档并未解释如何实现这一点,而且笔者认为这一设置似乎并无多大实用性。
 
Deployer用于集群的farm功能,监控应用中文件的更新,以保证集群中所有节点应用的一致性,如某个用户上传文件到集群中某个节点的应用程序目录下,Deployer会监测到这一操作并把这一文件拷贝到集群中其他节点相同应用的对应目录下以保持所有应用的一致。这是一个相当强大的功能,不过很遗憾,tomcat集群目前并不能做到这一点,开发人员正在努力实现它,这里的配置只是预留了一个接口。
 
ClusterListener用于跟踪集群中节点发出和收到的数据,也有点类似Valve的功能。


下面通过假想的一组场景来描述tomcat集群如何工作,集群采用默认配置,由t1和t2两个tomcat例程组成,场景按照时间顺序排列。
1. t1启动
    t1按照标准的tomcat启动,当Host对象被创建时,一个Cluster对象(默认配置下是SimpleTcpCluster)也同时被关联到这个Host对象。当某个应用在web.xml中设置了distributable时,Tomcat将为此应用的上下文环境创建一个DeltaManager。SimpleTcpCluster启动membership服务和Replication服务(用于建立tcp连接)。


2. t2启动(待t1启动完成后)
    首先t2会执行和t1一样的操作,然后SimpleTcpCluster会建立一个由t1和t2组成的membership。接着t2向集群中已启动的服务器即t1请求Session数据,如果t1没有响应t2的拷贝请求,t2会在60秒后time out。在Session数据拷贝完成之前t2不会接收客户端的http或mod_jk/ajp请求。


3. t1接收http请求,创建Session s1
    t1正常响应客户请求,但是在t1把结果发送回客户端时,ReplicationValve会拦截当前请求(如果filter中配置了不需拦截的请求类型,这一步就不会进行,默认配置下拦截所有请求),如果发现当前请求更新了Session,调用Replication服务建立tcp连接把Session拷贝到membership列表中的其他节点即t2,返回结果给客户端(注意,如果采用同步拷贝,必须等拷贝完成后才会返回结果,异步拷贝在数据发送到tcp连接就返回结果,不等待拷贝完成)。在拷贝时,所有保存在当前Session中的可序列化的对象都会被拷贝,而不仅仅是发生更新的部分。


4. t1崩溃
    当t1崩溃时,t2会被告知t1已从集群中退出,然后t2就会把t1从自己的membership列表中删除,发生在t2的Session更新不再往t1拷贝,同时负载均衡器会把后续的http请求全部转发给t2。在此过程中所有的Session数据不会丢失。


5. t2接收s1的请求
    t2正常响应s1的请求,因为t2保存着s1的所有数据。


6. t1重新启动
    按步骤1、2一样的操作启动,加入集群,从t2拷贝所有Session数据,拷贝完成后开放自己的http和mod_jk/ajp端口接收请求。


7. t1接收请求,s1失效
    t1继续接收来自s1的请求,把s1设置为过期。这里的过期并非因为s1处于非活动状态超过设置的时间,而是执行类似注销的操作而引起的Session失效。这时t1并非发送s1的所有数据而是一个类似s1 expired的消息,t2收到消息后也会把s1设为过期。


8. t2接收请求,创建Session s2
    和步骤3一样。


9. t1 s2过期
    对于因超时引起的Session失效t1无需通知t2,因为t2同样知道s2已经超时。因此对于tomcat集群有一点非常重要,所有节点的操作系统时间必须一致!不然会出现某个节点Session已过期而在另一节点此Session仍处于活动状态的现象。


补充:
Membership 中:
            address:指定多播地址;
            port:指定端口号;
            frequency:节点间心跳信息传送的频率;
            dropTime:设置节点为不可用节点的超时时间;

Receiver中:
            address:在哪个地址上完成心跳信息传递和节点数据传输;
            port:指定端口号;
            autoBind:自动绑定,一次性可以接受多少个;
            selectorTimeout:挑选(如节点是否可用)的超时时间;
            maxThreads:最大线程数;

 



如果对整体的Enige做cluster就将上述配置放到Enige中,如果只对特定的主机做cluster就放到特定的Host中;


3.具体配置
3.1编辑配置文件
tomcatA的server.xml文件内容如下:
[root@localhost conf]# cat server.xml
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="80" address="192.168.85.129" maxThreads="1024"
               protocol="org.apache.coyote.http11.Http11AprProtocol"
               connectionTimeout="60000"
               redirectPort="443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="443" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatA">
    <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="228.0.100.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.85.129"
                      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.MessageDispatch15Interceptor"/>
          </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.JvmRouteSessionIDBinderListener"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
    </Cluster>
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>
tomcatB的server.xml文件内容如下:
[root@localhost conf]# cat server.xml
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  <Listener className="org.apache.catalina.core.JasperListener" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>
  <Service name="Catalina">
    <Connector port="80" address="192.168.85.130" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcatB">
    <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="228.0.100.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.85.130"
                      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.MessageDispatch15Intercepto"/>
          </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.JvmRouteSessionIDBinderListener"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
    </Cluster>
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log." suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>

  


 
3.2编辑web.xml文件
根据tomcat7.0官方资料中的说明,有这么一句话
Make sure your web.xml has the <distributable/>element,继续配置各节点的web.xml,使tomcatA和tomcatB的web.xml文件中加上一句;

3.3检查语法并重启tomcat后测试
注意:
如果出错请查看日志catalina.out,我也出现了错误,查看官方文档才知道是我的两个tomcat时间不一致:
Make sure that all nodes have the same time and sync with NTP service! #时间同步

Make sure that your loadbalancer is configured for sticky session mode. #负载均衡要配置会话保持

而且有时有必要在各节点为使用组播地址添加组播路由,格式为:
route add -net 组播地址 netmask 255.255.255.255 dev eth0

测试结果:
无论怎么刷新,都是一个页面



参考文档:


http://zyycaesar.iteye.com/blog/296606
posted @ 2022-11-18 09:49  小洛兵  阅读(94)  评论(0)    收藏  举报