轻量级别的Cache和反向代理软件---Varnish
1、Varnish描述
1.1 Varnish的结构与特点
Varnish是一个轻量级别的Cache和反向代理软件,先进的设计理念和成熟的设计框架是Varnish的主要特点:
- 基于内存进行缓存,重启后数据将消失
- 利用虚拟内存方式,I/O性能好.
- 支持设置0~60秒的精确缓存时间
- VCL配置管理比较灵活
- 32位机器上缓存文件大小为最大2GB
- 具有强大的管理功能,例如:top stat admin list等
- 状态设计巧妙,结构清晰
- 利用二叉堆管理缓存文件,达到可以积极删除目的。
1.2 Varnish与Squid的对比
说到Varnish,就不能不提Squid,Squid是一个高性能的代理缓存服务器,它和Varnish的相比较有诸多的异同点,下面进行分析:
相同点:
l 都是一个反向代理服务器
l 都是开源软件
异同点,也是Varnish的优点:
l Varnish的稳定性很高,两者在完成相同的负荷的工作时,Squid发生的故障几率要高于Varnish,因为Squid需经常重启.
l Varnish访问速度更快,Varnish采用用了”Visual Page Cache”技术,所有缓存的数据都直接从内存读取,而且Squid是从硬盘读取缓存数据,因此Varnish访问速度更快
l Varnish可以支持更多的并发连接,因为Varnish的TCP连接要比Squid释放快,所以在高并发连接情况可以支持更多地TCP连接。
l Varnish可以通过管理端口,使用证则表达式清除部分缓存,而Squid做不到。
Varnish缺点如下:
l 在高并发状态下CPU,I/O和内存等资源开销都要高于Squid。
l Varnish进程一旦挂起,崩溃或者重启,缓存数据都会从内存当中完全释放,此时所有的请求会发送后端的WEB服务器,在高并发的情况下,这会给后端的服务器造成很大压力。
2、Varnish安装
2.1、安装前环境
Varnish-Server1 à10.0.0.201
Web—Server2à10.0.0.202
2.2、创建Varnish用户缓存目录和日志
# useradd varnish -s /sbin/nologin 创建varnish用户
# mkdir /varnish/cache –p 创建varnish缓存目录
# mkdir /varnish/log 创建varnish日志
# chown -R varnish:varnish /varnish/ 修改赋权组
2.3、安装PCRE
为了兼容正则表达式,编译 2.0以上版本时否则会报错pcre找不到。
# yum install gcc
# yum install gcc-c++ libstdc++-devel
# yum install -y httpd-devel pcre perl pcre-devel zlib zlib-devel GeoIP GeoIP-devel
# wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.34.tar.bz2
# tar -xvf pcre-8.34.tar.bz2
# cd pcre-8.34/
# ./configure --prefix=/usr/local/prce/
# make && make install
注:如果你在编译的vasnish中的bin目录没有发现varnishstat, varnishtop, varnishhist这个三个程序的话,是因为编译前没有安装与操作系统位数对应的ncurses-devel 安装ncurses-devel
yum install ncurses-devel.x86_64
然后再次编译varnish 即可
2.4、安装Varnish
# wget http://repo.varnish-cache.org/source/varnish-3.0.0.tar.gz
# tar -xzvf varnish-3.0.0.tar.gz
# cd varnish-3.0.0
#./configure --prefix=/usr/local/varnish PKG_CONFIG_PATH=/usr/lib/pkgconfig
# make && make install
# cd /usr/local/varnish/sbin
./varnishd -V
3、Varnish配置
3.1、VCL使用说明
VCL,即Varnish Configaution Language 用来定义Varnish的存储策略
VCL内置函数
(1)、vcl_recv函数
用于接收和处理请求,当请求到达并成功接收后被调用.
函数一般以如下几个关键字结束
pass表示进入pass模式,把请求控制权交给vcl_pass函数
pige 表示进入pige模式,把请求控制权交给vcl_pipe函数
error code[reason] 表示返回”code”给客户,并放弃处理该请求,”code”是错误的标识
,例如:200和405等,”reason”是错误信息
(2)、vcl_pipe函数
此函数在进入pipe模式时被调用,用于请求直接传至后端主机,在请求和返回内容没有改变的情况下,将不变的内容返回给客户端,直到这个连接被关闭。
此函数一般以如下几个关键字结束:
error code[reason]
pipe
(3)、vcl_pass函数
此函数在进入pass模式时被调用,用于请求直接传至后端主机,后端主机但应数据,将答应的数据传至客户端,但不进行任何缓存,当前连接下每次都返回最新的内容。
此函数一般以如下几个关键字结束:
error code[reason]
pass
(4)、lookup函数
表示在缓存中查找被请求的对象,并且根据查找的结果把控制权交给vcl_hit或者函数vcl_miss
(5)、vcl_hit函数
在执行lookup指令后,在缓存中找到请求的内容后将自动调用该函数
此函数一般以如下几个关键字结束:
fetch 表示从后端获取请求的内容,并且把控制权交给vcl_fetch函数
deliver 表示将找到的内容发送给客户,并且把控制权交给函数vcl_deliver
error code[reason]
pass
(6)、vcl_miss函数
在执行lookup指令后,在缓存中没有找到请求的内容时自动调用该方法,此函数可以于判断是否需要从后端服务器获取内容
此函数一般以如下几个关键字结束:
fetch:表示从后端获取请求内容,并且把控制权交给vcl_fetch函数
error code[reason]
pass
(7)、vcl_miss函数
在后端主机更新缓存并且获取内容后调用该方法,接着,通过判断获取的内容来决定是将内容放入缓存,还是直接返回给客户端,
此函数一般以如下几个关键字结束:
error code[reason]
pass
deliver
(8)、vcl_deliver函数
将在缓存中找到请求的内容发送给客户端调用此方法。
此函数一般以如下几个关键字结束:
error code[reason]
deliver
(9)、vcl_timeout函数
在缓存内容到期前调用此函数.
此函数一般以如下几个关键字结束:
discard:表示从缓存中清除内容
fetch
(10)、vcl_discard函数
在缓存内容到期后或者缓存空间不足时,自动调用该函数.
此函数一般以如下几个关键字结束:
keep:表示将内容继续保留在缓存当中
discard
2.3.2、配置Varnish配置实战
由于本版不同,Varnish配置文件的写法也存在一定的差异,Varnish3.x版本不但在配置写法上和2.x版本不同,修复了很多不应用BUG,本文以3.x版本为基准
Varnish安装完成后,默认的配置文件/usr/local/varnish/etc/varnish/default.vcl
此配置文件默认是全部注释掉的.配置文件如下:
先编辑配置文件
# vi /usr/local/varnish/etc/varnish/default.vcl
# This is a basic VCL configuration file for varnish. See the vcl()
# man page for details on VCL syntax and semantics.
#
# Default backend definition. Set this to point to your content
# server.
#
# This is a basic VCL configuration file for varnish. See the vcl()
# man page for details on VCL syntax and semantics.
#
# Default backend definition. Set this to point to your content
# server.
#
#设置后端WEB服务
backend webtest1 {
.host = "10.0.0.202";
.port = "";
.connect_timeout = 1s;
.first_byte_timeout = 5s;
.between_bytes_timeout = 2s;
}
#可以定义多台WEB
#backend webtest2 {
# .host = "192.168.100.6";
# .port = "";
# .connect_timeout = 1s;
# .first_byte_timeout = 5s;
# .between_bytes_timeout = 2s;
#}
#多台WEB可以定义负载均衡
#director lb_test random {
# {
# .backend = webtest1;
# .weight = ;
# }
# {
# .backend = test2;
# .weight = ;
# }
#}
#定义那些IP或者IP段具有访问权
acl purge {
"localhost";
"127.0.0.1";
"192.168.1.0"/;
"10.0.0.0"/;
}
sub vcl_recv {
#开启压缩模式,图片格式取消压缩
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|jpeg|flv)" ) {
remove req.http.Accept-Encoding;
remove req.http.Cookie;
} else if (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} else if (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
remove req.http.Accept-Encoding;
}
}
#根据host设置后端服务器
if (req.http.Host ~ "(?i)(www.test1.com|www.test2.com|10.0.0.201)") {
#set req.backend = lb_test; #如果开启负载用此项
set req.backend = webtest1;
}else
if (req.http.Host ~ "(?i)image.wdj.com"){
set req.backend = webtest1;
}else
{
error "Hostname not found";
}
#如果为purge请求,客户端ip不在访问列表中,返回405拒绝
if (req.request == "PURGE") {
if (!client.ip ~purge) {
error "Not Allowed";
}
#本地缓存查找
return(lookup);
}
#如果为GET请求,url后缀为jpg,png,gif等 取出cookie
if (req.request == "GET"&&req.url ~ "(?i)\.(jpg|png|gif|swf|jpeg|ico)$") {
unset req.http.cookie;
}
#如果GET请求,url为php,则穿过cache,不缓存
if (req.request =="GET"&&req.url ~ "(?i)\.php($|\?)"){
return (pass);
}
#简单防盗链
if (req.http.referer ~ "http://.*") {
if ( !(req.http.referer ~ "http://.*test1\.com"
|| req.http.referer ~ "http://.*test2\.com"
|| req.http.referer ~ "http://.*wdj\.com"
|| req.http.referer ~ "http://.*google\.com"
|| req.http.referer ~ "http://.*baidu\.com"
|| req.http.referer ~ "http://.*yahoo\.cn"
)) {
error "Not Found!";
}
}
#获取客户端ip
# if (req.restarts == ) {
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# }
#不是以下请求进入pipe模块
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
#不是GET 和HEAD请求不缓存
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization) {
/* Not cacheable by default */
return (pass);
}
return (lookup);
}
#
sub vcl_pipe {
return (pipe);
}
#
sub vcl_pass {
return (pass);
}
#使用url+host hash算法查找数据
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
return (hash);
}
# 如果请求为purge 将清除缓存
sub vcl_hit {
if (req.request == "PURGE") {
set obj.ttl = 0s;
error "Purged";
}
return (deliver);
}
sub vcl_miss {
return (fetch);
}
#
sub vcl_fetch {
if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Vary == "*") {
/*
* Mark as "Hit-For-Pass" for the next 2 minutes
*/
set beresp.ttl = s;
return (hit_for_pass);
}
if (beresp.http.Pragma ~"no-cache" ||
beresp.http.Cache-Control ~"no-cache" ||
beresp.http.Cache-Control ~"private") {
return (deliver);
}
#为特定格式文件设置缓存时间,有多种的可以直接在后米啊加
if (req.request == "GET"&&req.url ~ "(?i)\.(js|css|mp3|jpg|png|gif|swf|jpeg|ico)$") {
set beresp.ttl = 30d;
}
if (req.request == "GET"&&req.url ~ "(?i)\.(html|htm)$") {
set beresp.ttl = 1d;
}
return (deliver);
}
# 设置返回状态
sub vcl_deliver {
set resp.http.x-hits = obj.hits;
if (obj.hits > ) {
set resp.http.X-Cache = "Hit test.com";
}else {
set resp.http.X-Cache = "Miss test.com";
}
set resp.http.Server = "BWM";
return (deliver);
}
# 定义错误
sub vcl_error {
set obj.http.Content-Type = "text/html; charset=utf-8";
set obj.http.Retry-After = "";
synthetic {"
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>"} + obj.status + " " + obj.response + {"</title>
</head>
<body>
<h1>Error "} + obj.status + " " + obj.response + {"</h1>
<p>"} + obj.response + {"</p>
<h3>Guru Meditation:</h3>
<p>XID: "} + req.xid + {"</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
"};
return (deliver);
}
sub vcl_init {
return (ok);
}
sub vcl_fini {
return (ok);
}
default.vcl
4、Varnish运行
4.1、varnish启动
-u 以什么用运行
-g 以什么组运行
-f varnish配置文件
-a 绑定IP和端口
-s varnish缓存文件位置与大小
-w 最小,最大线程和超时时间
-t 缓存时间s
-T varnish管理端口,主要用来清除缓存
-p client_http11=on 支持http1.1协议
-P(大P) /usr/local/varnish/var/varnish.pid 指定其进程码文件的位置,实现管理
停止varnish
varnish两种缓存方式:
(1)基于malloc内存+swap交换模式
/usr/local/varnish/sbin/varnishd
-f /usr/local/varnish/etc/varnish/default.vcl -s malloc,200M -a 10.0.0.201:80
-w 1024,512000,10 -t 10.0.0.201:3000
(2)基于file是mmap的文件内存映射的机制.
/usr/local/varnish/sbin/varnishd
-u varnish -g varnish -f /usr/local/varnish/etc/varnish/default.vcl -a
10.0.0.201:80 -s file,/varnish/cache/varnish_cache.data,1G -w 1024,51200,10 -t
3600 -T 10.0.0.201:3000
企业里里面开启为100G
4.2、启动日志
# /usr/local/varnish/bin/varnishncsa
-w /varnish/log/varnish.log &
日志加入开机启动:
echo "/usr/local/varnish/bin/varnishncsa -w /data/varnish/logs/varnish.log &" >> /etc/rc.local
参数: -w 指定varnish访问日志要写入的目录与文件
4.3、缓存验证:
可以通过浏览器访问对应的网页来查看Varnish缓存的效果。如果Varnish缓存成功,第二次打开网页的速度会明显比第一次快,但是这种方式并不能够充分说明问题。下面用命令行方式,通过查看网页头来查看命中情况。
- [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html
- HTTP/1.1 200 OK
- Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1
- Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT
- ETag: "5e850b-616d-48b06c6031cc0"
- Content-Type: text/html
- Content-Length: 24941
- Date: Fri, 09 Jul 2010 08:29:16 GMT
- X-Varnish: 1364285597
- Age: 0
- Via: 1.1 varnish
- Connection: keep-alive
- X-Cache: MISS from www.ixdba.net #这里的“MISS”表示此次访问没有从缓存读取
再次打开这个页面,查看网页的头信息。
· [root@varnish-server ~]# curl -I http://www.ixdba.net/a/mz/2010/0421/11.html
· HTTP/1.1 200 OK
· Server: Apache/2.2.14 (Unix) PHP/5.3.1 mod_perl/2.0.4 Perl/v5.10.1
· Last-Modified: Sat, 10 Jul 2010 11:25:15 GMT
· ETag: "5e850b-616d-48b06c6031cc0"
· Content-Type: text/html
· Content-Length: 24941
· Date: Fri, 09 Jul 2010 08:30:35 GMT
· X-Varnish: 1364398612 1364285597
· Age: 79
· Via: 1.1 varnish
· Connection: keep-alive
· X-Cache: HIT from www.ixdba.net #由“HIT”可知,第二次访问此页面时,从缓存中读取内容,也就是缓存命中
参考URL:http://book.51cto.com/art/201202/314875.htm
5、Varnish 日志切割
5.1、日志切割
# vim /data/shell/cut_varnish_log.sh
#!/bin/sh
logs_path=/varnish/logs
vlog=${logs_path}/varnish.log
date=$(date -d "yesterday" +"%Y-%m-%d")
pkill - varnishncsa
mkdir -p ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/
mv /varnish/logs/varnish.log ${logs_path}/$(date -d "yesterday" +"%Y")/$(date -d "yesterday" +"%m")/varnish-${date}.log
/usr/local/varnish/bin/varnishncsa -w /varnish/logs/varnish.log &
使用计划任务,每天晚上凌晨00点运行日志切割脚本
echo "0 0 * * * /data/shell/cut_varnish_log.sh" >> /etc/crontab
6、Varnish日常管理
6.1、日常管理
/usr/local/varnish/bin/varnishadm -T 10.0.0.201:3000 purge "req.http.host ~ www.server110.com$ && req.url ~ /static/image/tp.php"
详细介绍:
10.0.0.201:3000 ###为被清除缓存服务器地址
www.server201.com ###为被清除的域名
/static/image/tp.php ###为被清除的url地址列表
清除所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url
^.*$
清除image目录下所有缓存
/usr/local/varnish/bin/varnishadm -T 192.168.9.201:3000 ban.url
/image/
查看Varnish服务器连接数与命中率
/usr/local/varnish/bin/varnishstat –n /varnish/cache/varnish_cache.data
7、Varnish内置公用变量
公用变量名称 含义
req.backend 指定对应的后端主机
server.ip
表示服务器端IP
client.ip
表示客户端IP
req.request 指定请求的类型,例如GET、HEAD、POST等
req.url
指定请求的地址
req.proto 表示客户端发起请求的HTTP协议版本
req.http.header 表示对应请求中的http头部信息
req. restarts 表示请求重启的次数,默认最大值为4
Varnish
在向后端主机请求时,可以使用的公用变量如表3所示:
表3
公用变量名称 含义
beresp.request 指定请求的类型,例如GET、HEAD等
beresp.url 指定请求的地址
beresp .proto 表示客户端发起请求的HTTP协议版本
beresp .http.header 表示对应请求中的http头部信息
beresp .ttl 表示缓存的生存周期,也就是cache保留多长时间,单位是秒
从cache或者后端主机获取内容后,可以使用的公用变量如表4所示:
表4
公用变量名称 含义
obj.status 表示返回内容的请求状态代码,例如200、302、504等
obj.cacheable 表示返回的内容是否可以缓存,也就是说,如果HTTP返回是200、203、300、301、302、404、410等,并且有非0的生存期,则可以缓存
obj.valid 表示是否是有效的HTTP应答
obj.response 表示返回内容的请求状态信息
obj.proto 表示返回内容的HTTP协议版本
obj.ttl 表示返回内容的生存周期,也就是缓存时间,单位是秒
obj.lastuse 表示返回上一次请求到现在的间隔时间,单位是秒
对客户端应答时,可以使用的公用变量如表5所示:
表5
公用变量名称 含义
resp.status 表示返回给客户端的HTTP状态代码
resp.proto 表示返回给客户端的HTTP协议版本
resp.http.header 表示返回给客户端的HTTP头部信息
resp.response 表示返回给客户端的HTTP状态信息
在上面的讲述中,我们只是介绍了常用的VCL内置公用变量,如果需要了解和使用更多的公用变量信息,请登录varnish官方网站查阅。
轻量级别的Cache和反向代理软件---Varnish的更多相关文章
- 高级运维(一):反向代理&使用Varnish加速Web
案例1.反向代理 目标: 1.代理服务器可以将远程的Web服务器页面缓存于本地 2.代理服务器端口设置为80端口 3.用户通过访问代理服务器即可获得远程Web服务器页面上的内容 4.远程We ...
- 简单的html js node 前端直接使用反向代理软件
先放上已经打包好的地址 https://gitee.com/Amengxiaoya/node-proxy.git 切记 proxyConfig.json 设置代理 ip为自己的ipv4地址 (cmd ...
- 正向代理 forward proxy、反向代理 reverse proxy、透明代理 transparent proxy
https://zh.wikipedia.org/wiki/反向代理 反向代理在计算机网络中是代理服务器的一种.服务器根据客户端的请求,从其关系的一组或多组后端服务器(如Web服务器)上获取资源,然后 ...
- [转]反向代理过程与Nginx特点详解
原文链接:<Nginx搭建反向代理服务器过程详解> 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内 ...
- nginx反向代理缓存服务器的构建
一:代理服务可简单的分为正向代理和反向代理: 正向代理:用于代理内部网络对Internet的连接请求(如VPN/NAT),客户端指定代理服务器,并将本来要直接发送给目标Web服务器的HTTP请求先发送 ...
- 利用 squid 反向代理提高网站性能(转载)
本文在介绍 squid 反向代理的工作原理的基础上,指出反向代理技术在提高网站访问速度,增强网站可用性.安全性方面有很好的用途.作者在具体的实验环境下,利用 DNS 轮询和 Squid 反向代理技术, ...
- 多级反向代理下,Java获取请求客户端的真实IP地址多中方法整合
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...
- 反向代理在Web渗透测试中的运用
在一次Web渗透测试中,目标是M国的一个Win+Apache+PHP+MYSQL的网站,独立服务器,对外仅开80端口,网站前端的业务系统比较简单,经过几天的测试也没有找到漏洞,甚至连XSS都没有发现, ...
- nginx做反向代理负载均衡 Java怎么获取后端服务器获取用户IP
nginx做反向负载均衡,后端服务器获取真实客户端ip 首先,在前端nginx上需要做如下配置: location / proxy_set_hearder host ...
随机推荐
- sysbench使用教程【转载】
水晶命匣 2016-08-16 20:02 一.环境描述 此次使用的虚拟机环境如下所示: CPU:双核 2.4GHz 内存:4 GB 硬盘:120 GB IP:192.168.21.129 操作系统: ...
- 概率好题 Light OJ 1027
题目大意:你在迷宫里,有n扇门,每个门有一个val,这个val可正可负,每次通过一扇门需要abs(x)分钟,如果这个门的val是正的,那么就直接出了迷宫,否则回到原地,问出去迷宫的期望是多少? 思路: ...
- centos gdb安装
1. 下载gdb 7.6.1源码包 http://ftp.gnu.org/gnu/gdb/gdb-7.6.1.tar.gz 将源码包放在home目录的Download目录中 2. 解压缩gdb 7.6 ...
- 转 如何不耍流氓的做运维之——SHELL脚本
家都是文明人,尤其是做运维的,那叫一个斯文啊.怎么能耍流氓呢?赶紧看看,编写 SHELL 脚本如何能够不耍流氓. 下面的案例,我们以 MySQL 数据库备份 SHELL 脚本的案例来进行阐述. 不记录 ...
- /var/lib/mysql/mysql.sock错误的解决办法
问题描述: 使用mysql -uroot -p登录出现找不到 /var/lib/mysql/mysql.sock或者被使用的问题. 可以用如下命令登录:mysql -p --socket=/tmp/m ...
- c3p0连接池的使用
利用c3p0连接池获取数据库连接,即不再通过DriverManager的getConnection(url,user,password)方法获取connection,而是通过c3p0数据源的类来获取连 ...
- Office2003/2010等集成SP的简单方法
Office2003集成SP的简单方法 需要准备的工具:Office 2003 光盘镜像.SP3更新包.Office 2003 序列号.UltraISO,7-zip或winrar,虚拟光驱 步骤一:提 ...
- PAT1001
时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B Calculate a + b and output the sum in standard format 计算a+b ...
- 开源企业管理软件 ONES
ONES 不是 ONS,基于AngularJS + ThnkPHP开发的企业管理系统平台,名字可以理解为ONES is a Niubility ERP System 或者 ONES Notonly a ...
- 第13章 Swing程序组件----常用布局管理器
在Swing中,每个组件在容器中都有一个具体的位置和大小,而在容器中摆放各种组件时很难判断其具体位置和大小.布局管理器提供了Swing组件安排.展示在容器中的方法及基本的布局功能. Swing提供的常 ...