通过VMWARE建立LVS+NGINX+TOMCAT+MYSQL+NFS架构入门

通过VMWARE建立LVS+NGINX+TOMCAT+MYSQL+NFS架构入门


一 母本虚拟机建立(别名:母本A)

1.CentOS设置开机自动启动sshd服务。
查看sshd是否启动:/etc/init.d/sshd status
查看sshd是否已是系统服务:chkconfig --list |grep sshd

使用如下命令设置sshd服务自动启动:chkconfig --level 3 sshd on

===========重点说明=========

开启ssh后,如果是内网网络,那么可能会出现通过ssh访问很慢,因为无法访问DNS服务器。

修改一下SSH的配置,关闭DNS,另外按网上的说法,连GSSAPI验证一并关掉

  1. vi /etc/ssh/sshd_config  
将UseDNS和GSSAPIAuthentication都设置为no

  1. #GSSAPIAuthentication yes   
  2. GSSAPIAuthentication no  
  3. #UseDNS yes   
  4. UseDNS no 
然后,重启SSH服务

  1. /etc/init.d/sshd restart  
===========END===========

2.关闭图形界面启动
vim /etc/inittab 文件中:
:id:5:initdefault:(默认的 run level 等级为 5,即图形界面)5 修改为 3 即可
找回网卡 eth0,虚拟机每clone一次,网卡mac地址更新,centos自动新增一个eth网卡
rm -f /etc/udev/rules.d/70-persistent-net.rules
reboot
关闭服务NetworkManager
chkconfig NetworkManager off
service NetworkManager stop
vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0 #网卡设备名称
ONBOOT=yes #启动时是否激活 yes|no
BOOTPROTO=static #协议类型 dhcp bootp none
IPADDR=192.168.134.136 #网络IP地址
NETMASK=255.255.255.0 #网络子网地址
GATEWAY=192.168.134.1  #网关地址
BROADCAST=192.168.134.255 #广播地址
TYPE=Ethernet #网卡类型为以太网
启动network服务
chkconfig network on
service network start



二 克隆LVS(通过 母本A 克隆)

1 服务器LVS1配置准备
找回网卡 eth0,虚拟机每clone一次,网卡mac地址更新,centos自动新增一个eth网卡,修改ip:192.168.134.136
rm -f /etc/udev/rules.d/70-persistent-net.rules
reboot
每台机器根据自己的需要修改主机名:
便于操作时识别主机
#hostname lvs1
vi /etc/hosts
127.0.0.1               lvs1.localdomain localhost
::1             lvs1.localdomain6 localhost6
vi /etc/sysconfig/network
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=lvs1.localdomain


2 设置虚拟机,加桥接网卡能上网
ifcofnig -a
vi /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
BOOTPROTO=dhcp 
TYPE=Ethernet
3 Piranha安装(LVS图形化管理工具)
Yum加速:yum install yum-fastestmirror
yum install ipvsadm modcluster piranha system-config-cluster php php-cli php-common
安装完成后,会生成最主要的几个文件分别是:/etc/sysconfig/ha/lvs.cf,/etc/init.d/piranha-gui,/etc/init.d/pulse
关掉上网桥接网卡
/etc/init.d/piranha-gui start
Piranha安装效果
测试方法:http://192.168.134.136:3636
测试方法:curl 127.0.0.1:3636
设置piranha密码
/usr/sbin/piranha-passwd
123456


4 防火墙打开80,3636端口
关闭掉selinux  
#/usr/sbin/setenforce 0 立刻关闭 SELINUX
#/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT  
#/sbin/iptables -I INPUT -p tcp --dport 3636 -j ACCEPT
#/etc/rc.d/init.d/iptables save


5 查看DNS是否已配置到nameserver,防止无法能访问网络(有时候不加也可以)
[root@localhost ~]# vi /etc/resolv.conf


nameserver 202.98.96.68
nameserver 61.139.2.69




Piranha配置:DR模式
http://192.168.134.136:3636


点击global settings,如下:
Primary server public ip:输入你公网IP。


点击redundancy,这里是配置lvs服务器的冗余,


点击virtual servers,配置Virtual IP Address跟real server:


然后将所有的配置激活。
主备LVS复制配置文件
scp /etc/sysconfig/ha/lvs.cf  192.168.134.137:/etc/sysconfig/ha/lvs.cf
/etc/init.d/piranha-gui start
/etc/init.d/pulse start
LVS安装效果
/usr/share/nginx/html/index.html  加入不同服务器标志


http://192.168.134.200


ipvsadm


热备切换
LVS服务自启动配置
chkconfig piranha-gui on
chkconfig pulse on


reboot


Piranha相关轮询配置参数讲解及效果演示
Round robin:轮转调度 轮询 (最简单的轮询)
Weighted least-connections:加权最少连接(考虑负载、服务器性能因素)
Weighted round robin:加权最少连接(考虑服务器性能因素)
Least-connection:最少连接(考虑负载因素)
Locality-Based Least-Connection Scheduling:基于局部性的最少连接(考虑负载、服务器性能因素,另考虑目的ip地址连接保持)
Locality-Based Least-Connection Scheduling (R):带复制的基于局部性的最少连接(考虑负载、服务器性能因素,另考虑目的ip地址连接保持,还考虑超负载的情况)
Destination Hash Scheduling:目的地址散列调度 (根据目标地址锁定服务器)
Source Hash Scheduling:源地址散列调度(根据源地址锁定服务器)


/etc/sysconfig/ha/lvs.cf  文件配置说明
serial_no = 26              #序号。
primary = 192.168.134.136     #指定主Director Server的真实IP地址,是相对与有备用的Director Server而言的,也就是给Director Server做HA Cluster。
service = lvs               #指定双机的服务名。
backup_active = 1       #是否激活备用Director Server。“0”表示不激活,“1”表示激活。
backup = 192.168.134.137             #这里指定备用Director Server的真实IP地址,如果没有备用Director Server,可以用“0.0.0.0”代替。
heartbeat = 1           #是否开启心跳,1表示开启,0表示不开启。
heartbeat_port = 539     #指定心跳的UDP通信端口。
keepalive = 6               #心跳间隔时间,单位是秒。
deadtime = 8                #如果主Director Server在deadtime(秒)后没有响应,那么备份Director  Server就会接管主Director Server的服务。
network = direct            #指定LVS的工作模式,direct表示DR模式,nat表示NAT模式,tunnel表示TUNL模式。
debug_level = NONE          #定义debug调试信息级别。
virtual http{      #指定虚拟服务的名称。
active = 1     #是否激活此服务。
address = 192.168.134.200 eth0:1  #虚拟服务绑定的虚拟IP以及网络设备名。
     vip_nmask = 255.255.255.0


port = 80                   #虚拟服务的端口。
send = "GET / HTTP/1.0\r\n\r\n"  #给real server发送的验证字符串。
expect = "HTTP"             #服务器正常运行时应该返回的文本应答信息,用来判断real server是否工作正常。
use_regex = 0               # expect选项中是否使用正则表达式,0表示不使用,1表示使用。
load_monitor = none         #LVS中的Director Server能够使用 rup 或 ruptime 来监视各个real server的负载状态。该选项有3个可选值,rup、ruptime和none,如果选择rup,每个real server就必须运行rstatd服务。如果选择了ruptime,每个real server就必须运行 rwhod 服务。

scheduler = wlc              #指定LVS的调度算法。 wlc加权最少连接
protocol = tcp              #虚拟服务使用的协议类型。
timeout = 6                 #real server失效后从lvs路由列表中移除失效real server所必须经过的时间,以秒为单位。
reentry = 15                #某个real server被移除后,重新加入lvs路由列表中所必须经过的时间,以秒为单位。
quiesce_server = 0          #如果此选项为1.那么当某个新的节点加入集群时,最少连接数会被重设为零,因此LVS会发送大量请求到此服务节点,造成新的节点服务阻塞,建议设置为0。
     server nginx1 {                #指定real server服务名。
address = 192.168.134.140    #指定real server的IP地址。


active = 1                  #是否激活此real server服务。
port = 80
weight = 1                   #指定此real server的权值,是个整数值,权值是相对于所有real server节点而言的,权值高的real server处理负载的性能相对较强。
}
server nginx2 {
address = 192.168.134.140
active = 1
port = 80
weight = 1
}
}


====================可能会出现的问题pulse dead but pid file exists=========
这是由于selinux规则设置问题,我比较懒直接把selinux配置文件修改,就解决了


查看SELinux状态:
1、/usr/sbin/sestatus -v      ##如果SELinux status参数为enabled即为开启状态
SELinux status:                 enabled
2、getenforce                 ##也可以用这个命令检查
关闭SELinux:
1、临时关闭(不用重启机器):
setenforce 0                  ##设置SELinux 成为permissive模式
                              ##setenforce 1 设置SELinux 成为enforcing模式


2、修改配置文件需要重启机器:(我采用这种方式)
修改/etc/selinux/config 文件
将SELINUX=enforcing改为SELINUX=disabled
重启机器即可






======================================================


三 克隆NGINX(通过 母本A 克隆)

1 找回网卡 eth0,虚拟机每clone一次,网卡mac地址更新,centos自动新增一个eth网卡,修改ip:192.168.134.140
rm -f /etc/udev/rules.d/70-persistent-net.rules
reboot
每台机器根据自己的需要修改主机名:
便于操作时识别主机
#hostname nginx1
/etc/hosts
/etc/sysconfig/network
2 设置虚拟机,加桥接网卡能上网
ifcofnig -a
vi /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
BOOTPROTO=dhcp 
TYPE=Ethernet
Yum加速:yum install yum-fastestmirror
http://nginx.org/en/download.html
3 添加nginx源:
 wget http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
rpm -ivh nginx-release-centos-6-0.el6.ngx.noarch.rpm
yum install nginx
nginx的几个默认目录
1 配置所在目录:/etc/nginx/
2 PID目录:/var/run/nginx.pid
3 错误日志:/var/log/nginx/error.log
4 访问日志:/var/log/nginx/access.log
5 默认站点目录:/usr/share/nginx/html
4 防火墙打开80端口 
关闭掉selinux  
#/usr/sbin/setenforce 0 立刻关闭 SELINUX
#/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT  
5 DR模式包返回伪装配置


#iptables -t nat -A PREROUTING -p tcp -d 192.168.52.213 --dport 80 -j REDIRECT
#/etc/rc.d/init.d/iptables save
============================重点说明Start=========================
如果虚ip没配置正确,或者把iptables关闭,都可以造成DR模式下访问虚IP不成功的情况(比如:可以ping通虚IP,但无法访问的情况)
可以通过命令
service iptables status
查看nat表里面的规则是不是我们的虚ip
Table: nat
Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            192.168.52.213      tcp dpt:80 
或者用# iptables -t nat -vnL PREROUTING --line-number 查看Chain PREROUTING


如果不是虚IP。那么就用以下进行删除
iptables -t nat -D PREROUTING 1(1为nat表的num号)
用以下命令保存
#/etc/rc.d/init.d/iptables save


最后再通过返回伪装配置进行添加虚IP。然后保存。

关于iptables的基本操作请参考:http://blog.csdn.net/johnstrive/article/details/26044861

-----------------------------------------------------------------------------------------------------------

关于nginx proxy_next_upstream导致的一个重复提交错误

一个请求被重复提交,原因是nginx代理后面挂着2个服务器,请求超时的时候(其实已经处理了),结果nigix发现超时,有把请求转给另外台服务器又做了次处理。

配置:proxy_next_upstream off

后问题解决,不过也会带来另外问题,比如发布的时候部分请求会出错等。

=============================重点说明End=========================


6 测试安装是否成功
http://192.168.134.140
curl 127.0.0.1:80
wget 127.0.0.1:80 
7 启动nginx,并加入自启动服务
service nginx status
service nginx start
chkconfig nginx on
reboot


nginx 配置文件检测  nginx -t


Nginx调度策略
nginx 的upstream目前支持4种方式的分配
1、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器 ,如果后端服务器down掉,能自动剔除。
2、weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。  例如:
    upstream bakend {
         server 192.168.0.14 weight=10;
         server 192.168.0.15 weight=10;
    }
3、ip_hash 
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session 的问题。例如:
    upstream bakend {
         ip_hash;
         server 192.168.0.14:88;
         server 192.168.0.15:80;
    }
4、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
upstream backend {
    server server1;
   server server2;
     fair;
}
5、url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。
例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法
upstream backend {
    server squid1:3128;
    server squid2:3128;
    hash   $request_uri;
    hash_method crc32;
}




upstream bakend{#定义负载均衡 设备的Ip及设备状态
ip_hash;
    server 127.0.0.1:9090 down;
    server 127.0.0.1:8080 weight=2;
    server 127.0.0.1:6060;
    server 127.0.0.1:7070 backup;
}
在需要使用负载均衡的server中增加
proxy_pass http://bakend/ ;
小技巧参数说明为:
1.down 表示当前的server暂时不参与负载
2.weight 默认为1.weight越大,负载的权重就越大。
3.max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误
4.fail_timeout:max_fails次失败后,暂停的时间。
5.backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。
nginx支持同时设置多组的负载均衡,用来给不用的server来使用。
client_body_in_file_only 设置为On 可以讲client post过来的数据记录到文件中用来做debug
client_body_temp_path 设置记录文件的目录 可以设置最多3层目录
location 对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡
下面四种情况分别用http://hello.com/css/test.html 进行访问。
第一种:
location  /css/ {
          proxy_pass http://tomcat/;
}
会被代理到http://192.168.134.142:8080/test.html 这个url 


第二种(相对于第一种,最后少一个 /)
location  /css/ {
          proxy_pass http://tomcat;
}
会被代理到http://192.168.134.142:8080/css/test.html 这个url 


第三种:
location  /css/ {
          proxy_pass http://tomcat/js/;
}
会被代理到http://192.168.134.142:8080/js/test.html 这个url。
 
第四种情况(相对于第三种,最后少一个 / ):
location  /css/ {
          proxy_pass http://tomcat/js;
}
会被代理到http://192.168.134.142:8080/jstest.html 这个url
 
带“/”就会把 http://servername:port/path/替换掉






Nginx对URL进行匹配
语法规则: location [=|~|~*|^~] /uri/ { … }
= 开头表示精确匹配 
^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可。nginx不对url做编码,因此请求为/static/20%/aa,可以被规则^~ /static/ /aa匹配到(注意是空格)。 
~ 开头表示区分大小写的正则匹配 
~*  开头表示不区分大小写的正则匹配 
!~和!~*分别为区分大小写不匹配及不区分大小写不匹配 的正则 
/ 通用匹配,任何请求都会匹配到。 
多个location配置的情况下匹配顺序为: 
首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。
“$”符号表示URL结尾
例子,有如下匹配规则例子:
location = / {
#规则A
        proxy_pass http://tomcat/index;
}
location = /login {
#规则B
        proxy_pass http://tomcat/login.jsp;
}
location ^~ /static/ {
#规则C
        root   /usr/share/nginx/html;
        index  index.html index.htm;
}
location ~ \.(gif|jpg|png|js|css)$ {
#规则D
        root   /usr/share/nginx/html;
}
location ~* \.png$ {
#规则E
       proxy_pass http://tomcat;
}
location ~\.shtml$ {
#规则F
       root   /usr/share/nginx/html;
      if (!-e $request_filename) {
        proxy_pass http://tomcat;
      }
}
location ~*\.shtml$ {
#规则G
        root   /usr/share/nginx/html;
      if (!-e $request_filename) {
        proxy_pass http://tomcat;
        }
}
location / {
#规则H
     proxy_pass http://tomcat;
}
访问根目录/, 比如http://hello.com/ 将匹配规则A
访问http://hello.com/login将匹配规则B,http://hello.com/register则匹配规则H
访问http://hello.com/static/a.html将匹配规则C
访问http://hello.com/a.gif,http://hello.com/b.jpg将匹配规则D和规则E,但是规则D顺序优先,规则E不起作用, 而http://hello.com/static/c.png则优先匹配到 规则C
访问http://hello.com/a.PNG则匹配规则E, 而不会匹配规则D,因为规则E不区分大小写。
访问http://hello.com/a.shtml不会匹配规则F和规则G,http://hello.com/a.SHTML不会匹配规则G,因为不区分大小写。规则F,规则G属于排除法,符合匹配规则但是不会匹配到,所以想想看实际应用中哪里会用到。
访问http://hello.com/category/id/1111则最终匹配到规则H,因为以上规则都不匹配,这个时候应该是nginx转发请求给后端应用服务器,比如FastCGI(php),tomcat(jsp),nginx作为方向代理服务器存在。


Nginx对URL进行匹配-常用
所以实际使用中,至少有三个匹配规则定义常用,如下: 
直接匹配网站根,通过域名访问网站首页比较频繁,使用这个会加速处理,官网如是说。这里是直接转发给后端应用服务器了,也可以是一个静态首页
# 第一个必选规则
location = / {
    proxy_pass http://tomcat/index

# 第二个必选规则是处理静态文件请求,这是nginx作为http服务器的强项
# 有两种配置模式,目录匹配或后缀匹配,任选其一或搭配使用
location ^~ /static/ {
    root /webroot/static/;
}
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}


#第三个规则就是通用规则,用来转发动态请求到后端应用服务器
#非静态文件请求就默认是动态请求,自己根据实际把握
#毕竟目前的一些框架的流行,带.php,.jsp后缀的情况很少了
location / {
    proxy_pass http://tomcat/
}




Nginx一些可用的全局变量
$arg_PARAMETER #这个变量包含GET请求中,如果有变量PARAMETER时的值。
$args     #这个变量等于请求行中(GET请求)的参数,例如foo=123&bar=blahblah;
$binary_remote_addr #二进制的客户地址。
$body_bytes_sent #响应时送出的body字节数数量。即使连接中断,这个数据也是精确的。
$content_length #请求头中的Content-length字段。
$content_type #请求头中的Content-Type字段。
$cookie_COOKIE #cookie COOKIE变量的值
$document_root #当前请求在root指令中指定的值。
$document_uri #与$uri相同。
$host #请求主机头字段,否则为服务器名称。
$is_args #如果有$args参数,这个变量等于”?”,否则等于”",空值。
$http_user_agent #客户端agent信息
$http_cookie #客户端cookie信息
$limit_rate #这个变量可以限制连接速率。
$query_string #与$args相同。
$request_body_file #客户端请求主体信息的临时文件名。
$request_method #客户端请求的动作,通常为GET或POST。
$remote_addr #客户端的IP地址。
$remote_port #客户端的端口。
$remote_user #已经经过Auth Basic Module验证的用户名。
$request_completion #如果请求结束,设置为OK. 当请求未结束或如果该请求不是请求链串的最后一个时,为空(Empty)。
$request_method #GET或POST
$request_filename #当前请求的文件路径,由root或alias指令与URI请求生成。
$request_uri #包含请求参数的原始URI,不包含主机名 如:”/foo/bar.php?arg=baz”。不能修改。
$scheme #HTTP方法(如http,https)。
$server_protocol #请求使用的协议,通常是HTTP/1.0或HTTP/1.1。
$server_addr #服务器地址,在完成一次系统调用后可以确定这个值。
$server_name #服务器名称。
$server_port #请求到达服务器的端口号。
$uri #不带请求参数的当前URI,$uri不包含主机名,如”/foo/bar.html”。该值有可能和$request_uri 不一致。$request_uri是浏览器发过来的值。该值是rewrite后的值。例如做了internal redirects后。




Nginx对URL重写
三、ReWrite语法
last – 基本上都用这个Flag。
break – 中止Rewirte,不在继续匹配
redirect – 返回临时重定向的HTTP状态302
permanent – 返回永久重定向的HTTP状态301
1、下面是可以用来判断的表达式:
-f和!-f用来判断是否存在文件
-d和!-d用来判断是否存在目录
-e和!-e用来判断是否存在文件或目录
-x和!-x用来判断文件是否可执行
2、下面是可以用作判断的全局变量
例:http://localhost:88/test1/test2/test.php
$host:localhost
$server_port:88
$request_uri:http://localhost:88/test1/test2/test.php
$document_uri:/test1/test2/test.php
$document_root:D:\nginx/html
$request_filename:D:\nginx/html/test1/test2/test.php
四、Redirect语法
server {
listen 80;
server_name start.igrow.cn;
index index.html index.php;
root html;
if ($http_host !~ “^star\.igrow\.cn$&quot {
rewrite ^(.*) http://star.igrow.cn$1 redirect;
}
}
七、禁止访问某个目录
location ~* \.(txt|doc)${
root /data/www/wwwroot/linuxtone/test;
deny all;
}
点开头的文件禁止访问,避免以“点”开头的文件(例如.htaccess)被其他用户访问到
location ~ /. {    
 deny  all; 
}


五、防盗链location ~* \.(gif|jpg|swf)$ {
valid_referers none blocked start.igrow.cn sta.igrow.cn;
if ($invalid_referer) {
rewrite ^/ http://$host/logo.png;
}
}


Nginx动静分离URL转发


 Nginx代理后,服务端收到参数
Info.jsp 看一下nginx代理后tomcat收到的参数:
<%@ page language="java" import="java.util.*" pageEncoding="GBK"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Server Info</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
</head>
<body>
<%
String SERVER_NAME = request.getServerName();
String SERVER_SOFTWARE = getServletContext().getServerInfo();
String SERVER_PROTOCOL = request.getProtocol();
Integer SERVER_PORT = request.getServerPort();
String REQUEST_METHOD = request.getMethod();
String PATH_INFO = request.getPathInfo();
String PATH_TRANSLATED = request.getPathTranslated();
String SCRIPT_NAME = request.getServletPath();
String DOCUMENT_ROOT = request.getRealPath("/");
String QUERY_STRING = request.getQueryString();
String REMOTE_HOST = request.getRemoteHost();
String REMOTE_ADDR = request.getRemoteAddr();
String AUTH_TYPE = request.getAuthType();
String REMOTE_USER = request.getRemoteUser();
String CONTENT_TYPE = request.getContentType();
Integer CONTENT_LENGTH = request.getContentLength();
String HTTP_ACCEPT = request.getHeader("Accept");
String HTTP_USER_AGENT = request.getHeader("User-Agent");
String HTTP_REFERER = request.getHeader("Referer");
HashMap infoMap = new HashMap();
infoMap.put("SERVER_NAME", SERVER_NAME);
infoMap.put("SERVER_SOFTWARE", SERVER_SOFTWARE);
infoMap.put("SERVER_PROTOCOL", SERVER_PROTOCOL);
infoMap.put("SERVER_PORT", SERVER_PORT);
infoMap.put("REQUEST_METHOD", REQUEST_METHOD);
infoMap.put("PATH_INFO", PATH_INFO);
infoMap.put("PATH_TRANSLATED", PATH_TRANSLATED);
infoMap.put("SCRIPT_NAME", SCRIPT_NAME);
infoMap.put("DOCUMENT_ROOT", DOCUMENT_ROOT);
infoMap.put("QUERY_STRING", QUERY_STRING);
infoMap.put("REMOTE_HOST", REMOTE_HOST);
infoMap.put("REMOTE_ADDR", REMOTE_ADDR);
infoMap.put("AUTH_TYPE", AUTH_TYPE);
infoMap.put("REMOTE_USER", REMOTE_USER);
infoMap.put("CONTENT_TYPE", CONTENT_TYPE);
infoMap.put("CONTENT_LENGTH", CONTENT_LENGTH);
infoMap.put("HTTP_ACCEPT", HTTP_ACCEPT);
infoMap.put("HTTP_USER_AGENT", HTTP_USER_AGENT);
infoMap.put("HTTP_REFERER", HTTP_REFERER);
Iterator it = infoMap.keySet().iterator();
%>
<table border="1">
<%
while (it.hasNext()) {
Object o = it.next();
%>
<tr>
<td>
<%=o%>
<td>
<%=infoMap.get(o)%>
</td>
</tr>
<%
}
%>
</table>
</body>
</html>
Nginx对URL重写
标记:
last 相当于Apache里的[L]标记,表示完成rewrite,基本上都用这个Flag
break 终止匹配, 不再匹配后面的规则
redirect 返回302临时重定向 地址栏会显示跳转后的地址
permanent 返回301永久重定向 地址栏会显示跳转后的地址


判断文件或目录:
-f和!-f用来判断是否存在文件
-d和!-d用来判断是否存在目录
-e和!-e用来判断是否存在文件或目录
-x和!-x用来判断文件是否可执行




正则表达式语法


Nginx对URL重写
1.简单例子: rewrite "/zixun/([0-9]+)(/*).html$" /zixun/$1/ last;
“/zixun/([0-9]+)(/*).html$”为正则表达式匹配你输入的url地址,表示/zixun/任意数字,至少出现一次,/出现0次或者多次,以.html结尾
/zixun/$1/ last; 符合以上规则的url 转发到 /zixun/$1/到这个链接上,这个就是你实现要获得数据的链接了 ,last为后面的不进行匹配了
如:http://www.xx.con/zixun/56.html 会把这个请求转发到 www.xx.con/zixun/56/的servlet上获得数据


2.多目录转成参数 abc.hello.com/sort/2 => abc.hello.com/info.jsp?act=sort&name=abc&id=2
if ($host ~* (.*)\.hello\.com) {
set $sub_name $1;   
rewrite ^/sort\/(\d+)\/?$ /info.jsp?act=sort&cid=$sub_name&id=$1 last;
}
3.目录对换 /123456/xxxx/ -> /xxxx.jsp?id=123456
rewrite ^/(\d+)/(.+)/ /$2.jsp?id=$1 last;


4.例如下面设定nginx在用户使用ie的使用重定向到/nginx-ie目录下:
if ($http_user_agent ~ MSIE) {
rewrite ^(.*)$ /nginx-ie/$1 break;
}


5.目录自动加“/”
if (-d $request_filename){
rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
}


6.禁止htaccess
location ~/\.ht {
         deny all;
     }
7.禁止多个目录
location ~ ^/(cron|templates)/ {
         deny all;
 break;
     }
8.文件反盗链并设置过期时间 这里的return 412 为自定义的http状态码,默认为403,方便找出正确的盗链的请求 “rewrite ^/ http://hello.com/leech.gif;”显示一张防盗链图片
“access_log off;”不记录访问日志,减轻压力 “expires 3d”所有文件3天的浏览器缓存
location ~* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ {
valid_referers none blocked *.hello.com localhost;
if ($invalid_referer) {
    rewrite ^/ http://hello.com/leech.gif;
    return 412;
    break;
}
                 access_log   off;
                 root /opt/lampp/htdocs/web;
expires 3d;
break;
     }
9.域名跳转
server     {
             listen       80;
             server_name  h.com;
             index index.html index.htm;
             root  /usr/share/nginx/html;
             rewrite ^/ http://www.baidu.com/;
             access_log  off;
     }


10.多域名转向
server_name  hello.com hello.net ~(.*).hello.com;
             index index.html index.htm index.php;
             root   /usr/share/nginx/html;
if ($host ~ “hello\.net") {
rewrite ^(.*) http://hello.com/net$1 redirect;
}




11.三级域名跳转
if ($http_host ~* "^(.*)\.i\.hello\.com$") {
rewrite ^(.*) http://top.hello.com$1;
break;
}


12.域名镜向
server     {
             listen       80;
             server_name  mirror.hello.com;
             index index.html index.htm;
             root  /usr/share/nginx/html;
             rewrite ^/(.*) http://www.hello.com/$1 last;
             access_log  off;
     }
13.某个子目录作镜向
location ^~ /test{
  rewrite ^.+ http://tst.hello.com/ last;
  break;
     }

====nginx 简单的动静分离配置======
upstream mxpro {
ip_hash;
server 10.1.1.61:8080;
server 10.1.1.62:8080;
}
server{
listen 80;
server_name 10.1.1.64; //vip 地址

location ~* \.(action|jsp) {
proxy_pass http://mxpro;
}
location ~* \.(gif|jpg|png|js|css)$ {
root /usr/share/nginx/html/;
}
        location /yyjx/ {
                proxy_pass http://mxpro;
        }
location / {
                root /usr/share/nginx/html;
                index index.html;
        }
}


======================






NFS共享文件系统实战

nfs安装
yum install nfs-utils rpcbind
mkdir /opt/centos6
cd /opt/centos6/
mkdir /share
cat  /etc/exports 
vi /etc/exports
/opt/centos6 *(rw,no_root_squash)
共享目录    所有ip都可以访问(可读写,不需要root转义也就是都是root)
chkconfig nfs on
/etc/init.d/rpcbind start
/etc/init.d/nfs start


service iptables stop
chkconfig nfs off 
rpcinfo –p


Nginx1上nfs客户端安装
yum install nfs-utils rpcbind
showmount -e 192.168.134.138
mkdir /opt/centos6
mount -t nfs 192.168.134.138:/opt/centos6/ /opt/centos6/
看nfs共享效果
cd /opt/centos6/
设置开机自动挂载NFS共享目录
vi /etc/rc.local 
mount -t nfs -o nolock 192.168.134.138:/opt/centos6 /opt/centos6


tomcat1上nfs客户端安装
apt-get install nfs-common
showmount -e 192.168.134.138
mkdir /opt/centos6
mount -t nfs 192.168.134.138:/opt/centos6/ /opt/centos6/
看nfs共享效果
cd /opt/centos6/
ls
设置开机自动挂载NFS共享目录
vi /etc/rc.local 
mount -t nfs -o nolock 192.168.134.138:/opt/centos6 /opt/centos6




mysql主从复制安装配置

mysql安装
yum -y install mysql-server
vim /etc/my.cnf
[mysqld]
character-set-server = utf8
[mysql]
default-character-set = utf8
chkconfig --list mysqld
chkconfig mysqld on
service mysqld start
service mysqld status
mysql设置root密码
mysql -u root
mysql> use mysql


mysql> select user,host,password from mysql.user;  ← 查看用户信
mysql> update user set password=password(‘123456') where user='root';
mysql> flush privileges;
mysql> quit
Mysql遗忘密码如何重置
service mysqld stop


mysqld_safe --skip-grant-tables &


mysql -u root
mysql> use mysql
mysql> update user set password=password(‘123456') where user='root';
mysql> flush privileges;
mysql> quit


service mysqld restart
Clone生成服务器mysql2
修改ip:192.168.134.145
每台机器根据自己的需要修改主机名:
便于操作时识别主机
#hostname mysql2
/etc/hosts
/etc/sysconfig/network
修改主从配置
修改主服务器master:
   #vi /etc/my.cnf
       [mysqld]
       log-bin=mysql-bin   //[必须]启用二进制日志
       server-id=144      //[必须]服务器唯一ID,默认是1,一般取IP最后一段


修改从服务器slave:
   #vi /etc/my.cnf
       [mysqld]
       log-bin=mysql-bin   //[必须]启用二进制日志
       server-id=145       //[必须]服务器唯一ID,默认是1,一般取IP最后一段
在主服务器上建立帐户并授权slave
service mysqld restart
/usr/local/mysql/bin/mysql -uroot -p
GRANT REPLICATION SLAVE ON *.* to ‘mysync’@‘%’ identified by ‘123456'; //一般不用root帐号,“%”表示所有客户端都可能连,只要帐号,密码正确,此处可用具体客户端IP代替,如192.168.134.145,加强安全。


登录主服务器的mysql,查询master的状态


 mysql>show master status;
 +------------------+----------+--------------+------------------+
   | File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
   +------------------+----------+--------------+------------------+
   | mysql-bin.000001 |      250 |              |                  |
   +------------------+----------+--------------+------------------+
   1 row in set (0.00 sec)


注:执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化
配置从服务器Slave
   mysql>change master to master_host='192.168.134.144',master_user=‘mysync',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=250;   //注意不要断开,“250”无单引号。   Mysql>start slave;    //启动从服务器复制功能
检查从服务器复制功能状态
mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.134.144 //主服务器地址
                  Master_User: mysync //授权帐户名,尽量避免使用root
                  Master_Port: 3306 //数据库端口,部分版本没有此行
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 250 //#同步读取二进制日志的位置,大于等于>=Exec_Master_Log_Pos
               Relay_Log_File: mysqld-relay-bin.000002
                Relay_Log_Pos: 251
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes      //此状态必须YES
            Slave_SQL_Running: Yes    //此状态必须YES
检查从服务器复制功能状态
         Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
          Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 250
              Relay_Log_Space: 407
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
1 row in set (0.00 sec)


 防火墙打开3306端口
关闭掉selinux  
#/usr/sbin/setenforce 0 立刻关闭 SELINUX
#/sbin/iptables -I INPUT -p tcp --dport 3306 -j ACCEPT
#/etc/rc.d/init.d/iptables save  




 允许其他服务器访问
mysql>GRANT   ALL   PRIVILEGES   ON   *.*   TO   'root'@'%'   WITH   GRANT   OPTION //赋予任何主机访问数据的权限
mysql>FLUSH   PRIVILEGES //修改生效
mysql>EXIT //退出MySQL服务器
主从服务器测试
主服务器Mysql,建立数据库,并在这个库中建表插入一条数据:
  mysql> create database hello_db;
  Query OK, 1 row affected (0.00 sec)
  mysql> use hello_db;
  Database changed
  mysql>  create table hello_tb(id int(3),name char(10));
  Query OK, 0 rows affected (0.00 sec)
  mysql> insert into hello_tb values(001,‘hello');
  Query OK, 1 row affected (0.00 sec)


 mysql> show databases;
   +--------------------+
   | Database           |
   +--------------------+
   | information_schema |
   | hello_db              |
   | mysql              |
   | test               |
   +--------------------+
   4 rows in set (0.00 sec)
若主服务器有老数据的情况


停止主数据的的更新操作,并生成主数据库的备份,我们可以通过mysqldump导出数据到从数据库,当然了,你也可以直接用cp命令将数据文件复制到从数据库去
注意在导出数据之前先对主数据库进行READ LOCK,以保证数据的一致性


mysql> flush tables with read lock;


之后是mysqldump
mysqldump -h127.0.0.1 -p3306 -uroot –p123456 > /home/tom/test.sql


最好在主数据库备份完毕,恢复写操作
mysql> unlock tables;


主从复制sql:scp /home/tom/test.sql  192.168.134.145: /home/tom/test.sql
从mysql导入:
mysql -u root -p123
mysql>source /home/tom/test.sql


mysql> start slave;


利用Mysql解决memcahe持久化问题
Mysql  jdbc 驱动jar包下载


http://www.mysql.com/products/connector/
Mysql测试mysqltest.jsp
<%@ page import="com.mysql.jdbc.Driver" %> 
<%@ page import="java.sql.*" %>
<% 
String driverName="com.mysql.jdbc.Driver"; 
String userName="hello"; 
String userPasswd="123456"; 
String dbName="hello_db"; 
String tableName="hello_tb"; 
//联结字符串 
String url="jdbc:mysql://192.168.134.144/"+dbName+"?user="+userName+"&password="+userPasswd; 
Class.forName("com.mysql.jdbc.Driver").newInstance(); 
Connection connection=DriverManager.getConnection(url); 
Statement statement = connection.createStatement(); 
String sql="SELECT * FROM "+tableName; 
ResultSet rs = statement.executeQuery(sql); 
ResultSetMetaData rmeta = rs.getMetaData(); 
int numColumns=rmeta.getColumnCount(); 
out.print("<br>表列数:"+numColumns+"<br>"); 
// 输出每一个数据值 
for (int i = 1;i<=numColumns;i++){
out.print(rmeta.getColumnName(i)); 
out.print("|"); 
}
out.print("<br>"); 
while(rs.next()) { 
for (int j = 1;j<=numColumns;j++){
out.print(rs.getString(j)); 
out.print("|"); 
}
out.print("<br>"); 

out.print("数据库操作成功,恭喜你"); 
rs.close(); 
statement.close(); 
connection.close(); 
%> 


为程序增加mysql用户及权限
mysql> insert into mysql.user(host,user,password) values("%","hello",password("123456"));


mysql> grant all privileges on hello_db.* to hello;


mysql> flush privileges;
为count在db中建立表进行存储
mysql> use hello_db
mysql> create table count_tb(c int);
mysql> insert into count_tb values(1);
mysql> update count_tb set c=100;
修改count.jspc.jsp
从memcached未取到数据,从db获取数据
 //first 未获取到,首次使用mysql获取数据
Class.forName("com.mysql.jdbc.Driver").newInstance(); 
Connection connection=DriverManager.getConnection(url); 
Statement statement = connection.createStatement(); 
String sql="SELECT c FROM "+tableName;
ResultSet rs = statement.executeQuery(sql); 
rs.next();
i = rs.getLong(1);
rs.close(); 
statement.close(); 
connection.close(); 
//以上代码从db中获取持久化的数据
        mcc.add("count", Long.valueOf(i)); 
long M = 1000*10;//间隔超过10秒存一次db
        Object updateTime = mcc.get("updateTime");        
        long currentTime = System.currentTimeMillis();
        if( (updateTime==null) || (currentTime-((Long)updateTime).longValue())>M ){
        Class.forName("com.mysql.jdbc.Driver").newInstance(); 
        Connection connection=DriverManager.getConnection(url); 
        Statement statement = connection.createStatement(); 
        String sql="UPDATE "+tableName+" SET c="+i;
        statement.execute(sql);
        statement.close(); 
        connection.close(); 
        if(updateTime==null){        
        mcc.add("updateTime", Long.valueOf(currentTime)); 
        }else{
        mcc.replace("updateTime", Long.valueOf(currentTime));         
        }

        }






Tomcat服务器安装

在Oracle网站上下载相应版本的JDK,比如jdk-8u20-linux-x64.rpm,然后使用rpm -ivh安装,
接下来编辑/etc/profile文件,在文件的最后添加以下几行
在 /etc/profile 中加入以下内容:
JAVA_HOME=/usr/java/jdk1.8.0_20
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
export JAVA_HOME PATH CLASSPATH
保存文件,然后运行命令:source /etc/profile使用profile生效。
最后使用命令java -version来确定安装是否成功。
安装Tomcat8
1)先安装JDK,过程见上一篇日志CentOS安装JDK。
(1)在http://tomcat.apache.org 下载Tomcat8的tar.gz版本,
然后命令:tar -zxvf apache-tomcat-8.0.12.tar.gz 解压到/usr目录下,并将目录名改为tomcat。然后执行命令chmod 777 tomcat。
(2)cp /usr/tomcat/bin/catalina.sh /etc/init.d/tomcat。
(3)vi /etc/init.d/tomcat
(4)在第二行添加以下两行
# chkconfig: 2345 10 90
# description:tomcat service
(5)添加以下两个环境变量。
CATALINA_HOME=/usr/tomcat
JAVA_HOME=/usr/java/jdk1.7.0_15
(6)保存。
(7)service tomcat start启动。
(8)在浏览器中测试:http://localhost:8080
(9)测试成功运行命令:chkconfig --add tomcat,将tomcat服务随系统启动。
 (10 ) vi catalina.sh 加入优化设置,参考如下
 JAVA_OPTS='-server -d64 -Xms2g -Xmx5g -XX:PermSize=1g -XX:MaxPermSize=2g -XX:+AggressiveOpts -XX:+UseParallelGC -XX:+UseBiasedLocking' 


安装MYSQL5.6

本文以MySQL-5.6.20 32位版本rpm格式的安装方式为例,详述MySQL的安装方式,64位的rpm版本安装方式也是如此。


    (一)删除老版本的MySQL
    在安装前要先确定系统是否已经安装了其他版本的MySQL,如已安装其他版本的MySQL,需先删除后再安装新版本。经本文亲测,采用如下方式删除老版本的MySQL或MySQL残留文件作为方便。
    1. 执行yum命令,删除MySQL的lib库,服务文件
yum remove  mysql mysql-server mysql-libs mysql-server;
    2. 执行find命令,查找MySQL的残留文件,然后运行“rm -rf 文件名”删除残留的MySQL文件
find / -name mysql
    (二)RPM格式安装MySQL


    当前,MySQL的最新版本为:5.6.20,从官网下载MySQL的rpm安装包,解压后有如下七个文件:
MySQL-client-advanced-5.6.20-1.el6.i686.rpm                          #MySQL客户端程序
MySQL-devel-advanced-5.6.20-1.el6.i686.rpm                            #MySQL的库和头文件
MySQL-embedded-advanced-5.6.20-1.el6.i686.rpm                  #MySQL的嵌入式程序
MySQL-server-advanced-5.6.20-1.el6.i686.rpm                          #MySQL服务端程序
MySQL-shared-advanced-5.6.20-1.el6.i686.rpm                        #MySQL的共享库
MySQL-shared-compat-advanced-5.6.20-1.el6.i686.rpm         #RHEL兼容包
MySQL-test-advanced-5.6.20-1.el6.i686.rpm                            #MySQL的测试组件


    一般对于开发而言,我们只需要下面三个文件就可以。


MySQL-client-advanced-5.6.20-1.el6.i686.rpm
MySQL-devel-advanced-5.6.20-1.el6.i686.rpm
MySQL-server-advanced-5.6.20-1.el6.i686.rpm
   
    1. 在重新进行安装之前,为确保万无一失,我们还是再确认一下系统中是否有MySQL极其相关的RPM安装包。如果有,则先删除。
rpm -qa | grep -i mysql
    执行完上述命令后,返回空数据,则可进行第二步。否则,执行下面的命令删除MySQL的相关包文件。
yum -y remove mysql-libs*
    2. 将前面提到的三个MySQL安装文件,拷贝到服务器,然后执行下述安装命令。
rpm -ivh MySQL-server-advanced-5.6.20-1.el6.i686.rpm
rpm -ivh MySQL-devel-advanced-5.6.20-1.el6.i686.rpm
rpm -ivh MySQL-client-advanced-5.6.20-1.el6.i686.rpm
    上述三个命令在执行时,只有第一个命令执行的时间稍微长些,后面两个命令运行速度很快。


    3.执行下述命令,将MySQL的配置文件拷贝到/etc目录下。
cp /usr/share/mysql/my-default.cnf /etc/my.cnf
    4.分别运行下述命令,初始化MySQL及设置密码。
/usr/bin/mysql_install_db  #初始化MySQL
service mysql start   #启动MySQL
cat /root/.mysql_secret      #查看root账号的初始密码,会出现下述所示信息
# The random password set for the root user at Mon Aug 25 10:26:57 2014 (local time): ZFRmqNPoFH3aO5PU
mysql -uroot -pZFRmqNPoFH3aO5PU       #使用root账号登陆MySQL
set password=password('123456');  #更改MySQL密码,注意;不可少
exit  #退出
mysql -uroot -p123456           #使用新密码登陆
    5.配置允许远程登陆。
use mysql;
mysql> select host,user,password from user;
+-----------+------+-------------------------------------------+
| host | user | password |
+-----------+------+-------------------------------------------+
| localhost | root | *DFEB299B8A17E376E7804A115F471149953B5645 |
| chenxu | root | *6E4C48EDF3CC66BC5F6E6C0980935C8ED660EFAA |
| 127.0.0.1 | root | *6E4C48EDF3CC66BC5F6E6C0980935C8ED660EFAA |
| ::1 | root | *6E4C48EDF3CC66BC5F6E6C0980935C8ED660EFAA |
+-----------+------+-------------------------------------------+
4 rows in set (0.00 sec)


mysql> update user set password=password('aqjccmtj') where user='root';
Query OK, 3 rows affected (0.00 sec)
Rows matched: 4 Changed: 3 Warnings: 0


mysql> update user set host='%' where user='root' and host='localhost';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0


mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> exit
Bye
    6.设置开机启动
[root@favccxx local]# chkconfig mysql on 
[root@favccxx local]# chkconfig --list | grep mysql 
mysql 0:off 1:off 2:on 3:on 4:on 5:on 6:off
    上面打印出来的内容中,2~5为on就是开机启动了。


    7.修改/etc/my.cnf,设置MySQL的字符集,配置MySQL表明不区分大小写(默认情况下,MySQL对表名区分大小写,列名不区分大小写)。在[mysqld]下面加入如下内容:
character_set_server=utf8
character_set_client=utf8 
collation-server=utf8_general_ci
lower_case_table_names=1
max_connections=1000
    8.MySQL的默认文件路径
/var/lib/mysql/               #数据库目录
/usr/share/mysql              #配置文件目录
/usr/bin                     #相关命令目录
#启动脚本
    9.重启MySQL
[root@favccxx local]# service mysql restart 
Shutting down MySQL.. SUCCESS! 
Starting MySQL. SUCCESS!


在Linux下安装MySQL是很简单的事情,但同时也是一种很繁琐的事情,谁让Linux上的操作大多数都要靠命令来完成呢。对于初学者来言,要记住所有的命令并不是多么明智的选择,最好的方法还是将一些常用的操作记录在笔记中。

 ===============重点说明=============

访问MySQL慢的处理

原因之一是每次访问db,mysql就会试图去解析来访问的机器的domain name,如果这时解析不到,等一段时间会失败,数据才能被取过来

在mysqld节增加一句话,不使用DNS即可,和SSH连接慢原理一样,这种连接慢的问题一般都是服务程序设置了DNS反向解析造成的。sshd,ftpd都是如此。教训。

  1. [mysqld]  
  2. skip-name-resolve  
重启mysql
  1. /etc/init.d/mysqld restart  
LINUX下的MYSQL默认是要区分表名大小写的。
  让MYSQL不区分表名大小写的方法其实很简单:
      1.用ROOT登录,修改/etc/my.cnf
  2.在[mysqld]下加入一行:lower_case_table_names=1
  3.重新启动数据库即可

===========END=====================

你可能感兴趣的:(通过VMWARE建立LVS+NGINX+TOMCAT+MYSQL+NFS架构入门)