nginx配置文件说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
##运行用户 [用户组]
user root;

##nginx进程数,建议设置为等于CPU总核心数
worker_processes 1;

##全局错误日志定义类型,[ debug | info | notice | warn | error | crit ]
error_log logs/error.log crit;
#error_log logs/error.log notice;
#error_log logs/error.log info;

##进程文件
pid logs/nginx.pid;

##一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。
worker_rlimit_nofile 1024;

events {
##参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络I/O模型,如果跑在FreeBSD上面,就用kqueue模型。
use epoll;

##单个进程最大连接数(最大连接数=连接数*进程数)
worker_connections 1024;
}

##设定http服务器
http {
##文件扩展名与文件类型映射表
include mime.types;

##反向代理配置
include ccproxy.conf;

##默认文件类型
default_type application/octet-stream;

##设置日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
##日志输出位置
access_log logs/access.log main;

##服务器名字的hash表大小
server_names_hash_bucket_size 256;

##设置读取客户端请求正文的缓冲容量.如果请求正文大于缓冲容量,整个正文或者正文的一部分将写入临时文件。 缓冲大小默认等于两块内存页的大小,在x86平台、其他32位平台和x86-64平台,这个值是8K。在其他64位平台,这个值一般是16K。
client_body_buffer_size 128k;

##设置读取客户端请求头部的缓冲容量。 对于大多数请求,1K的缓冲足矣。 但如果请求中含有的cookie很长,或者请求来自WAP的客户端,可能请求头不能放在1K的缓冲中。 如果从请求行,或者某个请求头开始不能完整的放在这块空间中,那么nginx将按照 large_client_header_buffers指令的配置分配更多更大的缓冲来存放。 directive.
client_header_buffer_size 8k;

##设置允许客户端请求正文的最大长度。请求的长度由“Content-Length”请求头指定。 如果请求的长度超过设定值,nginx将返回错误413 (Request Entity Too Large)到客户端。 请注意浏览器不能正确显示这个错误。 将size设置成0可以使nginx不检查客户端请求正文的长度。
client_max_body_size 50m;

##定义读取客户端请求头部的超时。如果客户端在这段时间内没有传送完整的头部到nginx, nginx将返回错误408 (Request Time-out)到客户端。
client_header_timeout 1m;

##定义读取客户端请求正文的超时。超时是指相邻两次读操作之间的最大时间间隔,而不是整个请求正文完成传输的最大时间。 如果客户端在这段时间内没有传输任何数据,nginx将返回408 (Request Time-out)错误到客户端。
client_body_timeout 1m;

##设置读取客户端请求超大请求的缓冲最大number(数量)和每块缓冲的size(容量)。 HTTP请求行的长度不能超过一块缓冲的容量,否则nginx返回错误414 (Request-URI Too Large)到客户端。 每个请求头的长度也不能超过一块缓冲的容量,否则nginx返回错误400 (Bad Request)到客户端。 缓冲仅在必需是才分配,默认每块的容量是8K字节。 即使nginx处理完请求后与客户端保持入长连接,nginx也会释放这些缓冲。
large_client_header_buffers 4 8k;

##设置向客户端传输响应的超时。超时仅指两次相邻写操作之间的时间间隔,而非整个响应的传输时间。 如果客户端在这段时间中没有收到任何数据,连接将关闭。
send_timeout 3m;

##开启或关闭使用sendfile()调用。
sendfile on;

##第一个参数设置客户端的长连接在服务器端保持的最长时间(在此时间客户端未发起新请求,则长连接关闭)。 第二个参数为可选项,设置“Keep-Alive: timeout=time”响应头的值。 可以为这两个参数设置不同的值。
##“Keep-Alive: timeout=time”响应头可以被Mozilla和Konqueror浏览器识别和处理。 MSIE浏览器在大约60秒后会关闭长连接。
keepalive_timeout 120;

##开启或关闭nginx使用TCP_NODELAY选项的功能。 这个选项仅在将连接转变为长连接的时候才被启用。(译者注,在upstream发送响应到客户端时也会启用)。
tcp_nodelay on;

##开启或者关闭nginx在FreeBSD上使用TCP_NOPUSH套接字选项, 在Linux上使用TCP_CORK套接字选项。 选项仅在使用sendfile的时候才开启。 开启此选项允许

##在Linux和FreeBSD 4.*上将响应头和正文的开始部分一起发送;
##一次性发送整个文件。
tcp_nopush on;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

upstream cctest1.com {
#server 127.0.0.1:9080 weight=5;
server 127.0.0.1:8080 weight=5;
#server 127.0.0.1:1111;
}

server {
listen 80;
server_name ccserver1;

#charset koi8-r;

access_log logs/host.access.log main;

index index.html index.htm index.jsp;

root /root/weir/apache-tomcat-8.0.15/webapps/ROOT/;


location ~* .*\.(jpg|jpeg|gif|png|swf|ico)$ {
if (-f $request_filename) {
#expires 15d;
break;
}
}
location ~* .*\.(html|htm|js|css)$ {
#expires 1d;
}
##匹配根下面的所有请求
location / {
proxy_pass http://cctest1.com;
}

#error_page 404 /404.html;

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

# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;

# location / {
# root html;
# index index.html index.htm;
# }
#}

# HTTPS server
#
#server {
# listen 443;
# server_name localhost;

# ssl on;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;

# ssl_session_timeout 5m;

# ssl_protocols SSLv2 SSLv3 TLSv1;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;

# location / {
# root html;
# index index.html index.htm;
# }
#}

}

spring源码解析-IOC容器(二)-读取配置文件

在Java中,将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源间的资源读取逻辑。而URL中却没有提供一些基本方法来实现自己的抽象结构。因而Spring提出了一套基于
org.springframework.core.io.Resource和org.springframework.core.io.ResourceLoader接口的资源抽象和加载策略。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
* Resource接口抽象了所有Spring内部使用到的底层资源:File、URL、Classpath等。
* 同时,对于来源不同的资源文件,Resource也有不同实现:文件(FileSystemResource)、Classpath资源(ClassPathResource)、
* URL资源(UrlResource)、InputStream资源(InputStreamResource)、Byte数组(ByteArrayResource)等等。
*
* @author Juergen Hoeller
* @since 28.12.2003
*/

public interface Resource extends InputStreamSource {

/**
* 判断资源是否存在
* @return boolean 是否存在
*/

boolean exists();

/**
* 判断资源是否可读
* @return boolean 是否可读
*/

boolean isReadable();

/**
* 是否处于开启状态
* @return boolean 是否开启
*/

boolean isOpen();

/**
* 得到URL类型资源,用于资源转换
* @return URL 得到URL类型
* @throws IOException 如果资源不能打开则抛出异常
*/

URL getURL() throws IOException;

/**
* 得到URI类型资源,用于资源转换
* @return URI 得到URI类型
* @throws IOException 如果资源不能打开则抛出异常
*/

URI getURI() throws IOException;

/**
* 得到File类型资源,用于资源转换
* @return File 得到File类型
* @throws IOException 如果资源不能打开则抛出异常
*/

File getFile() throws IOException;

/**
* 获取资源长度
* @return long 资源长度
* @throws IOException 如果资源不能打开则抛出异常
*/

long contentLength() throws IOException;

/**
* 获取lastModified属性
* @return long 获取lastModified
* @throws IOException 如果资源不能打开则抛出异常
*/

long lastModified() throws IOException;

/**
* 创建一个相对的资源方法
* @param relativePath 相对路径
* @return Resource 返回一个新的资源
* @throws IOException 如果资源不能打开则抛出异常
*/

Resource createRelative(String relativePath) throws IOException;

/**
* 获取文件名称
* @return String 文件名称或者null
*/

String getFilename();

/**
* 得到错误处理信息,主要用于错误处理的信息打印
* @return String 错误资源信息
*/

String getDescription();
}

nginx基本介绍

本文主要介绍一些Nginx的最基本功能以及简单配置。

1.静态HTTP服务器

首先,Nginx是一个HTTP服务器,可以将服务器上的静态文件(如HTML、图片)通过HTTP协议展现给客户端。
配置:

1
2
3
4
5
6
server {
listen 80; # 端口号
location / {
root /usr/share/nginx/html; # 静态文件路径
}
}

2、反向代理服务器

什么是反向代理?

客户端本来可以直接通过HTTP协议访问某网站应用服务器,如果网站管理员在中间加上一个Nginx,客户端请求Nginx,Nginx请求应用服务器,然后将结果返回给客户端,此时Nginx就是反向代理服务器。

配置:

1
2
3
4
5
6
server {
listen 80;
location / {
proxy_pass http://192.168.20.1:8080; # 应用服务器HTTP地址
}
}

既然服务器可以直接HTTP访问,为什么要在中间加上一个反向代理,不是多此一举吗?反向代理有什么作用?继续往下看,下面的负载均衡、虚拟主机,都基于反向代理实现,当然反向代理的功能也不仅仅是这些。

3、负载均衡

当网站访问量非常大,网站站长开心赚钱的同时,也摊上事儿了。因为网站越来越慢,一台服务器已经不够用了。于是将相同的应用部署在多台服务器上,将大量用户的请求分配给多台机器处理。同时带来的好处是,其中一台服务器万一挂了,只要还有其他服务器正常运行,就不会影响用户使用。

Nginx可以通过反向代理来实现负载均衡。

配置:

1
2
3
4
5
6
7
8
9
10
upstream myapp {
server 192.168.20.1:8080; # 应用服务器1
server 192.168.20.2:8080; # 应用服务器2
}
server {
listen 80;
location / {
proxy_pass http://myapp;
}
}

4、虚拟主机

网站访问量大,需要负载均衡。然而并不是所有网站都如此出色,有的网站,由于访问量太小,需要节省成本,将多个网站部署在同一台服务器上。
例如将www.aaa.com和www.bbb.com两个网站部署在同一台服务器上,两个域名解析到同一个IP地址,但是用户通过两个域名却可以打开两个完全不同的网站,互相不影响,就像访问两个服务器一样,所以叫两个虚拟主机。

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 80 default_server;
server_name _;
return 444; # 过滤其他域名的请求,返回444状态码
}
server {
listen 80;
server_name www.aaa.com; # www.aaa.com域名
location / {
proxy_pass http://localhost:8080; # 对应端口号8080
}
}
server {
listen 80;
server_name www.bbb.com; # www.bbb.com域名
location / {
proxy_pass http://localhost:8081; # 对应端口号8081
}
}

在服务器8080和8081分别开了一个应用,客户端通过不同的域名访问,根据server_name可以反向代理到对应的应用服务器。

原文地址:http://xxgblog.com/2015/05/17/nginx-start/

spring源码解析-IOC容器(一)

Spring的核心是IOC和AOP,IOC的本质是将资源文件(如applicationContext.xml)配置的bean(java对象)信息解析出来,然后放到BeanFactory(Spring容器)的Map中(这一步就是所谓的注册),这样以后程序就可以直接从BeanFactory中拿Bean的信息

为完成IOC容器初始化,Spring设计了层次化的类并使用一些设计模式来组织这样一个过程。在分析源码过程之前要对Spring的类图有一个大概的了解:

  • 图一:IOC容器主要类图

  • 图二:定义从外面加载资源的接口

  • 图三: bean的相关定义

以上3幅图来源于:链接,感谢^_^

Spring的IOC相关的源码解析基本上围绕上面3幅图的主要接口和类。Spring的IOC本质上就做了以下3件事:

  • 加载配置文件

  • 解析配置文件并注册Bean

  • 实例化Bean

nio入门

从JDK1.4开始,JDK提供了一套专门的类库支持非阻塞I/O,可以在java.nio包及其子包中找到相关的类和接口。由于这套API是新提供的I/O API,因此也叫New I/O,这就是JAVA NIO的由来。非阻塞IO API由3个主要部分组成:缓冲区(Buffers)、通道(Channels)和Selector

NIO服务端创建过程

  1. 打开ServerSocketChannel,用于监听客户端的连接,它是所有客户端连接的父管道

    1
    ServerSocketChannel acceptorSrv = ServerSocketChannel.open();
  2. 绑定监听端口,设置连接为非阻塞模式

    1
    2
    3
    acceptorSrv.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"), port));

    acceptorSrv.configureBlocking(false);
  3. 创建Reactor线程,创建多路复用器并启动线程

    1
    2
    3
    Selector selector = Selector.open();

    new Thread(new ReactorTask()).start();
  4. 将ServerSocketChannel注册到Reactor线程的多路复用器Selector上,监听ACCEPT事件

    1
    SelectionKey key = acceptorSvr.register(selector, SelectionKey.OP_ACCEPT, ioHandler);
  5. 多路复用器在线程run方法的无限循环体内轮询准备就绪的Key

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int num = selector.select();

    Set selectedKeys = selector.selectKeys();

    Iterator it = selectedKeys.iterator();

    while(it.hasNext()) {

    SelectionKey key = (SelectionKey) in.next();

    //...deal with I/O event...

    }
  6. 多路复用器监听到有新的客户端接入,处理新的接入请求,完成TCP三次握手,建立物理链路

    1
    SocketChannel channel = svrChannel.accept();
  7. 设置客户端链路的TCP参数

    1
    2
    3
    channel.configureBlocking(false);

    channel.socket().setReuseAddress(true);
  8. 将新接入的客户端连接注册到Reactor线程的多路复用器上,监听读操作

    1
    SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ, ioHandler);
  9. 异步读取客户端请求消息到缓冲区

    1
    int readNum = channel.read(receiveeBuffer);
  10. 对ByteBuffer进行编解码,如果有半包消息指针Reset,继续读取后续的报文,将解码成功的消息封装成Task,投递到业务线程池中,进行业务逻辑操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    Object message = null;

    while(buffer.hasRemain()) {

    byteBuffer.remark();

    message = decode(byteBuffer);

    if(message == null) {

    byteBuffer.reset();

    break;

    }

    messageLisk.add(message);

    }
  11. 将POJO对象encode成ByteBuffer,调用SocketChannel的异步write接口,将消息异步发送给客服端

    1
    socketChannel.write(buffer);

第48条:如果要精确的结果,请避免使用float和double

float和double类型主要是为了科学计算和工程计算而设计的。她们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结构,所以不应该被用于需要精确结果的场合。float和double类型尤其不适用于货币计算,因为要让一个float或者double精确地表示0.1(或者10的任何其它负数次方值)是不可能的。

例子:你现在有1元钱

货架上有一排糖果,标价为: 10分、20分、30分、40分、50分、60分…

你要从标价为10分的糖果开始,每种买1颗,一直到不能支付货架上下一种价格的糖果为止。傻子都看得出能买到4种糖果^_^(10+20+30+40),但是编程计算处理不当就会出问题!

错误的示范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public static void main(String[] args) {

double funds = 1.00;

int itemBought = 0;

for(double price = 0.10; funds >= price; price += 0.10) {

funds -= price;

itemBought++;

}

System.out.println("能买到" + itemBought + "种糖果!");

System.out.println("剩余金额 ¥" + funds);

}

运行上面这个程序你会发现你只能买到3中糖果,还剩余0.399999… GG! 出现这种情况的原因是float的计算精度造成的~

解决方案

1.使用BigDecimal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

public static void main(String[] args) {

final BigDecimal TEN_CENTS = new BigDecimal("0.10");
int itemBought = 0;

BigDecimal funds = new BigDecimal("1.00");

for(BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price.add(TEN_CENTS)) {
itemBought++;

funds = funds.substract(price);

}
System.out.println("能买到" + itemBought + "种糖果!");
System.out.println("剩余金额 ¥" + funds);
}

2.使用int或long

除了使用BigDecimal之外,还有一种方法是使用int或者long,到底使用int或者龙要取决于所涉及数值的大小,同时要自己处理十进制小数点。在这个例子中,最明显的做法是以分为单位进行计算,而不是以元为单位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public static void main(String[] args) {

int itemBought = 0;

int funds = 100;

for( int price = 10; funds >= price; price += 10) {

itemBought++;

funds -= price;

}

System. out.println( "能买到" + itemBought + "种糖果!" );

System. out.println( "剩余金额 ¥" + funds);

}

总结

  • 对于需要精确答案的计算任务,不要使用float或者double

  • 数值范围没有超过9位十进制数字,就可以使用int,如果不超过18位数字,就可以使用long,如果数值范围可能超过18位数字,就要使用BigDecimal

  • 使用BigDecimal有两个缺点,与使用基本运算类型相比,这样子很不方便,而且很慢。但是计算结果精确,允许你完全控制舍入(8种舍入)