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的更多相关文章

  1. 高级运维(一):反向代理&使用Varnish加速Web

    案例1.反向代理      目标: 1.代理服务器可以将远程的Web服务器页面缓存于本地 2.代理服务器端口设置为80端口 3.用户通过访问代理服务器即可获得远程Web服务器页面上的内容 4.远程We ...

  2. 简单的html js node 前端直接使用反向代理软件

    先放上已经打包好的地址 https://gitee.com/Amengxiaoya/node-proxy.git  切记 proxyConfig.json 设置代理 ip为自己的ipv4地址 (cmd ...

  3. 正向代理 forward proxy、反向代理 reverse proxy、透明代理 transparent proxy

    https://zh.wikipedia.org/wiki/反向代理 反向代理在计算机网络中是代理服务器的一种.服务器根据客户端的请求,从其关系的一组或多组后端服务器(如Web服务器)上获取资源,然后 ...

  4. [转]反向代理过程与Nginx特点详解

    原文链接:<Nginx搭建反向代理服务器过程详解> 1.1 反向代理初印象 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内 ...

  5. nginx反向代理缓存服务器的构建

    一:代理服务可简单的分为正向代理和反向代理: 正向代理:用于代理内部网络对Internet的连接请求(如VPN/NAT),客户端指定代理服务器,并将本来要直接发送给目标Web服务器的HTTP请求先发送 ...

  6. 利用 squid 反向代理提高网站性能(转载)

    本文在介绍 squid 反向代理的工作原理的基础上,指出反向代理技术在提高网站访问速度,增强网站可用性.安全性方面有很好的用途.作者在具体的实验环境下,利用 DNS 轮询和 Squid 反向代理技术, ...

  7. 多级反向代理下,Java获取请求客户端的真实IP地址多中方法整合

    在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实I ...

  8. 反向代理在Web渗透测试中的运用

    在一次Web渗透测试中,目标是M国的一个Win+Apache+PHP+MYSQL的网站,独立服务器,对外仅开80端口,网站前端的业务系统比较简单,经过几天的测试也没有找到漏洞,甚至连XSS都没有发现, ...

  9. nginx做反向代理负载均衡 Java怎么获取后端服务器获取用户IP

    nginx做反向负载均衡,后端服务器获取真实客户端ip   首先,在前端nginx上需要做如下配置: location / proxy_set_hearder host                 ...

随机推荐

  1. MySQL的数据类型【总结】

    1.时间类型 MySQL的DateTime,TimeStamp,Date和Time数据类型. DATETIME类型用在你需要同时包含日期和时间信息的值时.MySQL检索并且以'YYYY-MM-DD H ...

  2. tableView滚动的时候会 最后一行显示不完全的问题

    问题可能原因 1:tableView高度的设置不正确,应该是屏幕的高度减去上面的高度(包括状态栏以及navigationBar的高度).正确设置了tableView的高度之后,才可以正常滚动到最后一行 ...

  3. UML示例图

  4. ListView控件的Insert、Edit和Delete功能(第一部分)

    摘自:http://blog.ashchan.com/archive/2007/08/28/listview-control-insert-edit-amp-delete-part-1aspx/ Li ...

  5. zf-关于更换页面,的各种问题。

    问题1:找不到common 这个变量(集合)与layer这个js文件. 这里的common 就是一个方法集合,声明var common;  common.abc = function(参数1,参数2, ...

  6. EXP AND IMP

    purpors: exp pro data to uat data exp and imp will not create user expdp and impdp will create user ...

  7. EL表达式处理字符串 是否 包含 某字符串 截取 拆分...............

    EL表达式处理字符串 是否 包含 某字符串 截取 拆分............... JSP页面页头添加<%@ taglib uri="/WEB-INF/taglib/c.tld&qu ...

  8. display:table表格合并单元格

    直接上代码: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEn ...

  9. OpenCV 基础知识------图像创建、访问、转换

    cvCreateImage函数-- Cxcore数组操作 创建头并分配数据 IplImage* cvCreateImage( CvSize size, int depth, int channels ...

  10. HDU 2802 F(N)(简单题,找循环解)

    题目链接 F(N) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Sub ...