本版本为6.36版。如果有问题,请写信至:gm365365@sina.com
注意:本6.36版UFO只能采用(自带)3.23版UFOProbe,以前版本的UFOProbe不能监测本版本的UFO。
===================================================================================
启动篇
===================================================================================
1. 首先,您的计算机要安装JDK1.8.0_72或以上版本的JDK! 低版本的JDK将无法运行UFO;
2. 进入bin目录,Windows操作系统敲startup.bat, 回车。
注意:如果您在Windows下运行startup.bat而不能启动UFO,则用Notepad打开bin目录下的setclasspath.bat文件,其第4行是这样的:
rem set JAVA_HOME=D:\jdk1.8.0_192
把上面的那行的"rem "去掉,并且把"D:\jdk1.8.0_192"替换成您计算机里的JDK的安装目录(所谓安装目录就是含有bin的目录),然后重新运行startup.bat文件即可。
(下面的操作是在redhat/centOS下进行的,ubuntu下类似)
1. 从java.sun.com官方网站下载Linux平台的JDK: jdk-8u192-linux-x64.tar.gz;
2. 从www.gm365.com官方网站下载Linux平台的UFO Pro版:UFO_v6.36.tar.gz;
3. 把上面2个文件上传到你的Linux服务器的/usr/local目录下;
4. 安装JDK
# cd /usr/local
# chmod 755 jdk-8u192-linux-x64.tar.gz
# tar -zxvf jdk-8u192-linux-x64.tar.gz
5. 安装UFO Pro
# cd /usr/local
# chmod 755 UFO_v6.36.tar.gz
# tar -zxvf UFO_v6.36.tar.gz
6. 配置环境变量
# vi /etc/profile
(1) 在这个文件末尾加上
export JAVA_HOME=/usr/local/jdk1.8.0_192
export CLASSPATH=.:${JAVA_HOME}/jre/lib/rt.jar:${JAVA_HOME}/lib/dt.jar:${JAVA_HOME}/lib/tools.jar
export PATH=$PATH:${JAVA_HOME}/bin
注意:CentOS6、Redhat上面的是JAVAHOME,CentOS7、Unbuntu是{JAVA_HOME}。
(2) 利用下面命令使配置生效,并且查看JDK版本
# source /etc/profile
# java -version
7. 配置放火墙
# vi /etc/sysconfig/iptables
添加如下代码
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
重启iptables
service iptables restart
(注意:一些机房为了防止DDOS攻击,在机房本身还有一道放火墙,您要向机房申请对您的IP的80端口开通!)
8. 对.sh添加执行权限
# cd /usr/local/UFO_Pro/bin
# chmod a+x ./*.sh
9. 启动UFO
# cd /usr/local/UFO_Pro/bin
# ./startup.sh
(条件同Q1-2)
# cd /usr/local/UFO_Pro/bin
# ./shutdown.sh
(条件同Q1-2)
#vi /etc/rc.d/rc.local
添加以下内容:
export JAVA_HOME=/usr/local/jdk1.8.0_192
cd /usr/local/UFO_Pro/bin
./startup.sh
进入UFO的bin目录,敲shutdown.bat, 回车。
我不知道您怎么会有这样的需求。
关于UFO作为windows service的所有东西都在UFO的win-service子目录里。
要把UFO作为windows service安装或启动,您所要做的唯一事情是修改wrapper.conf, 一共需要修改4个地方(这4个地方都用一对"# !!!!!!!!!!!!!!!!!!"行标记起来):
1. wrapper.java.command=D:\jdk1.8.0_192\bin\java
根据您计算机的JDK,把wrapper.java.command指向java.exe(注意:设置时可不带.exe).
2. wrapper.java.classpath.3=D:\jdk1.8.0_192\lib\tools.jar
根据您计算机的JDK,把wrapper.java.classpath.3指向lib\tools.jar
3. wrapper.java.classpath.4=D:\UFO_Pro\lib\javax.zip
wrapper.java.classpath.5=D:\UFO_Pro\lib\mysql-connector-java-5.1.18-bin.jar
wrapper.java.classpath.6=D:\UFO_Pro\lib\UFO-db.zip
wrapper.java.classpath.7=D:\UFO_Pro\lib\UFO-jdt.jar
把classpath指向UFO的lib目录里的.zip和.jar.
4. wrapper.app.parameter.3=D:\UFO_Pro
把wrapper.app.parameter.3指向UFO的安装目录(所谓安装目录就是含有bin子目录的目录)。
在修改好wrapper.conf后,运行install_service.bat,安装成功后,再运行start_service.bat,
UFO就是作为windows service启动起来了。
这其实不是UFO的问题,而是Linux操作系统的问题。解决办法如下:
chmod a+x startup.sh
chmod a+x UFO.sh
chmod a+x setclasspath.sh
添加可执行权限即可。
(在Windows下)用startup.bat启动的UFO进程,Dos窗口不能关掉,否则,UFO进程也随之被kill掉。如果要使Dos窗口关掉后,UFO进程仍然在(后台)运行,应该怎么办?这时就应该用startupW.bat来启动UFO进程。
用startupW.bat启动的UFO进程,在计算机关机重启后,消失了,因此它所启动的不是UFO service.
用startupW.bat启动的UFO进程,要关掉它,(也)运行shutdown.bat; 要对它reload,(也)运行reload.bat。
===================================================================================
改动篇
===================================================================================
1. 6.30版支持异步Upload。
2. 6.30版支持sendfile。
3. 6.30版完善了小体积文件的cache问题。
4. 6.30版解决了一种假死情形。
5. 6.30版对soTimeout在NIO情形下做出正确的处理。
6. 6.30版调整了javax包和examples。
7. 6.09版改正了UFO的CPU有时会突然异常的问题。
8. 6.08版修改了UFOProbe方面的问题。
9. 6.00版支持智能负载均衡。
10. 6.00版的UFO Probe监控软件大升级。
11. 6.00版的监测和管理不再占用任何URL。
12. 6.00版的server.xml里配置参数精简。
13. 6.00版的负载能力有提高。
14. 自6.00版后,不再有Pro版和普通版,去掉了Pro版。
15. 5.61版修改了WebSocket方面的examples.
16. 5.60版支持conf/context.xml的Resource标签,支持jsp/servlet里JNDI用法:
InitialContext context=new InitialContext();
Queue queue=(Queue)context.lookup("java:comp/env/queue/queue0");
明显地,支持MQ和DataSource方式的数据库连接池。
17. 5.59版修改一个bug,以支持axis2 (WebService)。
18. 5.58版支持JDK1.9。
19. 5.57版改正一个jsp编译方面的错误。
20. 5.56版修改Request.getInputStream()和Request.getReader()方面的问题。
21. 5.55版解决展开.war很慢的问题。
22. 5.54版修改了标签方面的Bug。
23. 5.53版将静态文件的Cache从虚拟内存Cache改成了内存Cache,以缩短响应时间到最小,关于这点待补充文档
24. 5.53版修改了jsp的编译代码,以提供运行速度和负载能力。
25. 5.52版修改了编译器方面的问题。
26. 5.52版修改了静态资源文件在SpringServlet处理后不能输出的问题。
27. 5.50版对web.xml的<servlet-mapping>里的<url-pattern>/</url-pattern>给出正确的解释(与Tomcat一样),关于这点待补充文档。
28. 5.50版对<servlet-mapping>的 <servlet-name>default</servlet-name>给出正确的解释(与Tomcat一样),关于这点待补充文档。
29. 5.50版去掉了requestURL里”/servlet/”作为servlet标识符的做法,requestURL能否解释为servlet里唯一根据web.xml的<servlet-mapping>的情况来确定。
30. 5.50版将UFOProbe的windows安装版与UFO一起发布,而不再单独发布。
31. 5.50版支持了一些新的servlet规范。
32. 5.06版调整了log.txt文件的目录位置,现放在显眼位置。
33. 5.06版实现了log的1级,开发者可采用此级,然后将log.txt文件发给我公司,我公司很快定位问题。
34. 5.05版把各个文档、.bat、.conf里的jdk推荐版本从1.6.x修改成了1.8.y,以支持注解以及restlet等相关组件(经过测试,用jdk1.6.x以及以下版本jdk运行UFO,不能很好地支持注解)。
35. 5.05版更换了javax-ufo.zip和更新了examples里的联网扫雷。
36. 5.05版修改了收费规则,收费规则更成熟(见Q6-1至Q6-3)。
37. 5.02版支持web.xml里的<error-page>标签以及其它情况下出错的友好提示。
38. 5.01版再次修改Filter以支持Struts2。
39. 5.00版更换了ClassLoader。
40. 5.00版修改了Filter。
41. 5.00版修改了Request.getPathInfo()和Request. getContextPath()。
42. 5.00版调整了javax.zip和javax-ufo.zip。
43. 4.63版支持负载均衡,参见Q3-34和Q3-35。
44. 4.62版对Connector标签增加了soRcvBuf和soSndBuf两个属性。
45. 4.61版修改WebSocket在download大文件时特别消耗CPU和内存的问题。
46. 4.60版修改WebSocket方面的WSProcessor里的WSEndpoint wsEndpoint的Bug。
47. 4.59版修改了内核哈希表的问题。
48. 4.59版再次修改了联网扫雷的断线标准的一行代码。
49. 4.59版修改了examples里所有含有Image的servlet、jsp的代码。
50. 4.58版提高了网络连接常阻塞时的文件下载速度。
51. 4.58版修改了联网扫雷的断线标准部分的代码。
52. 4.57版解决4.56版不稳定的问题,就是说4.56版也是不稳定版本。
53. 4.56版解决4.55版不稳定的问题。
54. 4.55版提高一种情况下的WebSocket性能。
55. 4.53版在conf/server.xml里增加了<Download>标签,以调节大体积文件下载时的性能。(见Q3-41)
56. 4.53版对静态文件下载正确填写http头,以保证浏览器能断线续传。
57. 4.53版修改了CS模式的WebSocket例子,增加了"Host" http头(在Web Server有多于1个Host时起作用,见Q8-17)
58. 4.53版对"喀秋莎-联网扫雷"的RecordThread.java增加了boolean idle这个变量,请体会它的重要作用。
59. 4.52版修改了WebSocket的一个Bug。
60. 4.51版提高了UFO内核里的哈希表(查找)的性能。
61. 4.50版优化了WebSocket关于断线方面的性能。
62. 4.49版去掉了server.xml里的Connector标签的maxFormContentSize, maxFormContentKill,maxUpLoadContentSize, maxUpLoadContentSizeKill, maxOtherContentSize, maxOtherContentSizeKill, maxErrorContentSizeKill等7个属性,以简化配置。并把maxParameterNum这个属性改成了maxParameterSize, 增加了maxParameterSizeKill。
63. 4.48版确保在WebSocket的WSEndpoint.close(WSClient aclient)方法调后,WSProcessor不再出现这个aclient的onData()。
64. 4.47版对Bad Request在debug Mode="1"时开始在Dos窗口里显示原因。
65. 4.46版修改HttpServletRequest.getInputStream().read()方法的一个Bug。
66. 4.45版修改了“喀秋莎-联网扫雷”的源代码,主要是增加了MsgNo这个清理bkp消息的重要消息。
67. 4.45版对UFO启动不起来给出更详细的提示和log。
68. 4.44版修改websocket的一处内存泄漏问题。
69. 4.43版修改了ClassLoader,以试图根除同名类(但包名不同)所带来的麻烦。
70. 4.42版在examples开源给出一个基于CS模式的websocket开发的喀秋莎-联网扫雷网游1.00版。
71. 4.42版确保javax.ufo.ws.WSProcessor的onClose(WSClient client, String message)方法是在WSClient client对应的Socket被close之后调用。
72. 4.42版在javax.ufo.ws.WSEndpoint增加了getRemoteSocketAddress(WSClient aclient), setSoTimeout(WSClient aclient, int timeout)等常用的Socket方法。
73. 4.41版在examples开源给出一个基于CS模式的websocket开发的喀秋莎-联网扫雷网游0.80版。
74. 4.40版在examples开源给出一个基于CS模式的websocket开发的TT简易聊天联网应用。
75. 4.40版去掉了websocket的UFO层的HeartBeat检测。
76. 4.39版在UFO发布首页带上UFO Web Server论坛链接。
77. 4.39版不输出jsp的注释行。
78. 4.39版修改一个jsp编译方面的Bug。
79. 4.38版使第2次设置ServletResponse的CharacterEncoding无效,并打印出warnings信息。
80. 4.38版修改一个jsp编译方面的Bug。
81. 4.38版修改一个taglib方面的Bug。
82. 4.37版解决一些情况下的中文乱码问题。
83. 4.37版提供客户端方式的(而是不只是applet方式)的UFO Probe,运行probe目录下的UFOProbe.bat启动。
84. 4.36版在/conf/server.xml增加了<System>标签,可以把System.out.print的内容输出到指定的文件,或干脆关掉System.out.print以试图解决Windows进程莫明其妙地噎住的问题,就是增加UFO在Windows下的稳定性。
85. 4.35版在/conf/server.xml增加了<FileCache>标签,用户可以自己配置File Cache的体积。
86. 4.34版提高在网站含有较大的动态网页时网站输出网页的流畅度。
87. 4.33版在<Connector>的参数配小了出错时,给予友好提示。
88. 4.32版修改ClassLoader的一个错误。
89. 4.31版解决jsp规范中的FunctionMapper的麻烦。
90. 4.30版支持WebSocket功能。
91. 4.30版修改HttpServletRequest.getHeaders()的Bug。
92. 4.30版实现每个http连接占用内存2.0K。
93. 4.30版实现每个WebSocket连接占用内存1.0K。
94. 4.25版对错误提示更友好。
95. 4.24版把静态网页的MappedByteBuffer数从500降为5,以防止高并发时,UFO内存用光。
96. 4.23版在jsp里的EL表达式为null时不输出任何内容,而不是输出"null"。
97. 4.23版修改了在3.22版引入的一处可能不稳定的错误。
98. 4.22版提高输出网页的流畅度。
99. 4.21版再次修改ClassLoader以对付一个不常见的问题。
100. 4.20版修改在有filter时不能正确回应"304 NOT_MODIFIED"的问题。
101. 4.19版可以配置更改UFO Probe的admin目录。
102. 4.18版修改了ClassLoader、RequestDispatcher、filter等方面若干问题。
103. 4.17版修改一处从4.10版引入的不能输出网页的假死的Bug。
104. 4.16版修改jsp里含有java.sql的Statement时编译失败的问题。
105. 4.16版再次修改了welcome-file-list。
106. 4.15版再次修改ClassLoader。
107. 4.14版修改ClassLoader以加快Class装载速度,并解决第3方项目自带javax包中类的麻烦。
108. 4.13版修改welcome-file-list等Bug。
109. 4.12版修改https等若干Bug。
110. 4.11版在测试struts2/spring/hibernate/ZK/sitemesh的基础上,修改若干Bug.
111. 4.10版将https部分改用Nio实现。
112. 4.10版解决了session数据库不能保存attribute的问题。
113. 4.10版改正了HttpServletRequest的getSession()的一个Bug。
114. 4.10版记录了各个Context的_work目录下的jsp编译时的绝对路径,当发生项目copy迁移时,重新编译jsp(与UFO的编译机制有关)。
115. 4.10版最小化每个连接占用的内存,实现了每个http连接占用2.16K内存,每个https连接占用52K内存。
116. 4.10版支持web.xml里的<servlet-mapping>和<servlet>标签里的多个<url-pattern>属性写法。
117. 4.10版校对了HttpServletRequest的getServletPath()和getPathInfo()在各种情况的正确性。
118. 4.10版改正了HttpServletRequest的getRequestDispatcher(String path)在path以空格开始时不正确的问题。
119. 4.10版改正了HttpServletResponse的setLocale(Locale locale)在contentType为null时甩出Exception的问题。
120. 4.10版对<filter>和<web-resource-collection>标签里的<url-pattern>不再对"*"进行任意通配,而是只通配"/*", "x/*"和"*.x"等3种情况,这样可以减少CPU和内存,任意通配也并无实际需求意义。
121. 4.02版去掉了javax.zip里不必要的子目录,以避免与Spring等第3方jar包内容重复。
122. 4.01版修改Http请求无"If-Match"头但有"If-Modified-Since"和"If-None-Match"时,永远也回不出"304 NOT_MODIFIED"的Bug。
123. 4.00版内置特制的数据库,以支持任意多(可多达几百亿)个Session。
124. 4.00版支持Servlet的异步处理。
125. 4.00版大大改变了Connector标签的属性,以增加UFO的稳定性、防Hacker和提高性能。
126. 4.00版改正了Form认证的Bug。
127. 4.00版对Form认证的<form-login-config>标签增加了form-max-login-num属性,以增加UFO的稳定性。
128. 4.00版实现了Servlet 3.0规范的HttpServletRequest的getPart()和getParts()方法,对文件上传提供一个另外的支持。
129. 4.00版对Servlet的SingleThreadModel进行了标准的实现。
130. 4.00版支持web.xml里的<servlet>标签和<servlet-mapping>合并的写法。
131. 4.00版修改了Servlet和filter的url-pattern方面的一个Bug。
132. 4.00版支持IP屏蔽。
133. 4.00版修改了HttpServletRequest在getWriter()后而不setCharacterEncoding(),不能输出网页的Bug。
134. 4.00版大大修改了https部分的代码,以增加https模式下的稳定性和提高负载能力。
135. 4.00版在UFO线程满时,对UFO Probe用户启动新的线程为之服务。
136. 4.00版在Servlet(和jsp)的输出内容没超过8K缓存时,一定带上Content-Length的Http头。
137. 3.67版增强了一处的稳定性,预防一种Hacker攻击的可能。
138. 3.66版及时回收一种情况下的垃圾,保持运行内存最小。
139. 3.65版修改了conf/web.xml,增加了sis, cab, apk, ipa的mime-mapping配置,这样使用UFO可直接发布symbian, windows, android, iphone手机游戏。(不配置mime-mapping,手机端会把游戏包当网页打开,而不会安装)
140. Pro 3.64版在server.xml里增加<Host-Match>标签,以屏蔽一些域名。
141. Pro 3.63版对server.xml里的Connector标签的各个属性从程序上进行了校对,更好地防Hacker。
142. Pro 3.62版对Post方法请求静态网页时给予输出。
143. Pro 3.61版改正UFO一种可能宕掉的Bug。
144. Pro 3.60版重做了UFO的windows service部分。
145. Pro 3.60版对各个.bat和.sh文件进行了校对,并调整了发布目录结构。
146. Pro 3.60版不再要求站在UFO的bin目录下发出startup/reload/shutdown命令。在Linux下,可以站在任何目录位置发出命令,例如:./startup.sh, ./bin/startup.sh, /usr/local/UFO_Pro/bin/startup.sh都是正确的。在Windows下,要求站在UFO的安装目录或bin目录下发出startup/reload/shutdown命令,例如:startup.bat, bin\startup.bat都是正确的,而UFO_Pro\bin\startup.bat则是不正确的。
147. Pro 3.60版在Linux下启动多个UFO进程时,用ps -efw命令查找进程PID时得到了很好的区分。
148. Pro 3.60版修改了reload的Bug.
149. Pro 3.56版在程序内部检查UFO安装路径的正确性。
150. Pro 3.56版修改shutdown.sh有时不能kill UFO(尤其是在ubuntu操作系统下)的Bug。
151. Pro 3.54版修改UFO Web Server一种不能启动的情形。
152. Pro 3.53版支持Head方法(从而支持一些搜索引擎的蜘蛛爬虫)。
153. Pro 3.52版修改有关Post方法的一个Bug。(与Apache cxf项目,SOA有关)
154. Pro 3.51版修改pathInfo里含有"/"时,Request的getPathInfo(), getServletPath()返回值不对的Bug。(与Apache cxf项目有关)
155. Pro 3.51版修改RequestDispatcher.forward()后,Request的getQueryString()返回值不对的Bug。(与Apache cxf项目有关)
156. Pro 3.51版对web.xml里的<servlet-mapping>里的<url-pattern>的*通配符进行了标准实现。
157. Pro 3.50版支持ETag, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since等Http头。(控制利用浏览器端缓存、节省带宽、提高负载、提高静态网页响应速度,So, 聪明的网站应采用静态网页做首页,并且能使用静态网页的地方决不使用动态网页)。
158. Pro 3.50版支持If-Range, Range等Http头(断点续传)。
159. Pro 3.48版修改了GET方法的URL含有"?"时,Request的getPathInfo(), getServletPath(), getPathTranslated(), getRequestURL()返回值不对的问题(与axis和struts项目有关)。
160. Pro 3.48版修改了"Last-Modified: " http头的值不对的问题。
161. Pro 3.46版修改这样一个ClassCastException的问题:以前版本如果一个jsp里import了WEB-INF\classes\里的类,先访问这个jsp,然后在不重启UFO的情况下,修改这个jsp,再访问,可能throw ClassCastException的Bug。
162. Pro 3.45版支持gzip压缩。
163. Pro 3.45版支持"chunked" Transfer-Encoding,但从Client向Server端"chunked" Transfer-Encoding暂不支持(无这样的例子或需求)。
164. Pro 3.45版修改jsp/servlet的"Server"和"Date" Header重复输出的问题。
165. Pro 3.45版修改RequestDispatcher的forward在"File Not Found"时,显示空白页的Bug。
166. Pro 3.45版将/lib/javax.zip里的UFO自己的javax.db目录分离出来,形成UFO-db.zip,以方便用户用JDT开发调试程序。
167. Pro 3.45版增加了Connector标签的keepAlive等多个属性。
168. Pro 3.45版在server.xml里增加了Probe标签和属性。
169. Pro 3.45版在server.xml里增加了Session标签和属性。
170. Pro 3.40版填写ServletContext.getResourcePaths()方法,以支持Spring。
171. Pro 3.40版修改了UFO.bat、UFO.sh、UFOW.bat以及发布目录结构,以防止Spring(试图)重复装载类。
172. Pro 3.38版修改Reponse.flushBuffer()的Bug。(主要与Struts项目有关)
173. Pro 3.37版修改server.xml、web.xml等xml文件里有注释行时可能出错的Bug。
174. Pro 3.37版修改Request.getParameterMap().entrySet()不正确的Bug。(主要与Struts项目有关)
175. Pro 3.36版支持web.xml里的<error-page>属性。
176. Pro 3.36版正确设置Request的javax.servlet.include.servlet_path的属性值(与Struts的include有关)。
177. Pro 3.36版修改ServletContext的getResource()方法的一个Bug。
178. Pro 3.35版修改一处较严重的内存泄漏的Bug。
179. Pro 3.32版改正未在server.xml里显式配置Context的Dir和war部署的web.xml里的welcome-file无效的Bug。
180. Pro 3.32版改正把"/*"的url映射成Struts的Filter时,当url不以"/"结尾并且不是文件名时,访问不到welcome-file的Bug。
181. Pro 3.32版修改以前部分版本中由于“笔误”,在有多个Context时,配置Filter时可能引起混乱的Bug。
182. Pro 3.31版大大提高了ClassLoader从zip/jar包装载类的速度。
183. Pro 3.31版支持从zip/jar包装载Servlet类。
184. Pro 3.30版修改并优化了UFO的ClassLoader。
185. Pro 3.30版正确处理URL里含有"./"字符串的情形。
186. Pro 3.30版在conf/server.xml文件里<Debug mode="0" />时,当"File Not Found"时,以相对路径显示文件,这是为了防止向Hacker暴露Server采用何种操作系统。而在Debug mode为"1"等其它值时,当"File Not Found"时,以绝对路径显示文件。
187. Pro 3.30版的UFO Probe在UFO进程产生hs_err_pidxxx.log时,显示为要求重启的警告状态。
188. Pro 3.26版修改因CPU采点而使jsp/servlet编译变慢1s的问题。
189. Pro 3.25版修改web.xml里servlet标签和filter标签的init-param属性不起作用的Bug。
190. Pro 3.24版修改examples的数据库连接池的例子,并修改server.xml中一个注释行位置不对的问题,以免用户上帝纳闷。
191. Pro 3.23版UFO Probe正常显示CPU指标。
192. Pro 3.22版修改war部署时忘了处理web.xml文件的问题。
193. Pro 3.21版修改在Linux下敲./shutdown.sh有时不能Kill掉UFO进程的Bug。
194. Pro 3.20版修改UFO Probe的下行流量有时突然增大几十M(而实际上流量并没有增大)的问题。
195. Pro 3.19版修改Request.getParameterMap()相关的中文乱码问题。
196. Pro 3.18版增加struts的demo、EL表达式的demo。
197. Pro 3.17版修改把"/*"的url映射成Filter时,不能输出静态网页的Bug。本版本对支持struts进行了比较充分的测试。
198. Pro 3.16版针对支持struts,将UFO的两个对外接口规范化。
199. Pro 3.15版关于RequestDispatcher的Bug。
200. Pro 3.14版修改关于Filter的Bug。
201. Pro 3.13版对UFO Pro进程所使用的虚拟内存进行了很好的管理和调度,对虚拟内存的回收也进行了比较充分的测试。Pro 3.13版的性能评测指标与Pro 3.00版一样。而Pro 3.00-Pro 3.10是不稳定的版本。
202. Pro 3.10版修改Pro 3.01版和Pro 3.00版的一个严重的不稳定的Bug,就是说,Pro 3.01版和Pro 3.00版是一个不稳定的版本,请不用使用Pro 3.00版和Pro 3.01版!
203. Pro 3.01版修正在一种情况下Servlet不能装载的Bug,就是Ajax的demo中ProgressBar、ToolTip、YahooSearch不能运行的问题,这个Bug是在2.60版中引入的。
204. Pro 3.00版大幅提高输出静态网页的能力,特别是对较大体积的网页(>100kb的)。
205. Pro 3.00版大幅提高输出动态网页的能力,特别是对较大体积的jsp(>50kb的)。
206. 2.60版大幅降低运行内存,特别是对有很多个Context和Host的大项目。
207. 2.60版在编译了jsp/servlet后,内存泄漏大为下降,这样在UFO编译了jsp/servlet不重启也问题不大。
208. 2.60版动态装载更新的servlet class,以前版本虽然能动态编译servlet却不能动态装载。
209. 2.60版动态更新类路径,在UFO类路径上的类jar包、zip包发生增加,UFO编译时立即生效,而不需要重新启动UFO(而jar包、zip包发生更新、删除时,则要求重启动UFO)。
210. 2.60版修改https状态下不能显示图片验证码的Bug。
211. 2.51版在每天休闲时候执行一次System.gc()对UFO进程进行垃圾回收。
212. 2.50版采用eclipse的JDT Java编译器作为UFO的默认编译器。
213. 2.38版修改RequestDispatcher的相对路径的Bug。
214. 2.37版支持Debug模式,可显示必要的信息。
215. 2.36版修改jsp:include和jsp:forward的路径中含有"../"时的Bug。
216. 2.35版修改taglib和EL表达式方面的Bug。
217. 2.32版将jsp文件中的<!-- -->等注释行全盘输出,因为它们可能在一些场合不表示注释行的意思。
218. 2.31版修改war部署的Bug。
219. 2.30版修改Listener的一个Bug。
220. 2.30版就中文乱码问题对examples Context下的demo示例进行了校对。
221. 2.30版支持有图片验证码、download的jsp文件除了保存为编码方式为ANSI外,还可以保存为UTF-8、UTF-16LE、UTF-16BE等。
222. 2.30版支持server.xml文件里对<Connector>标签的URIEncoding属性。
223. 2.20版支持通过JNDI和DataSource来配置数据库连接池的做法(并在examples Context增加示例),从而实现对Tomcat等Web Server 100%的兼容。
224. 2.20版修改jsp, servlet实例池的UFO Probe监测数据略微异常的问题,使UFO的运行内存更小。
225. 2.20版对UFO进程结束时,进行更详细的写log文件。
226. 2.00版改正不能显示图片验证码的Bug,这个Bug是在1.90版中由于笔误引入的。
227. 2.00版支持动态编译servlet.java。
228. 1.99版修改当1500个点满时,UFO Probe显示紊乱的Bug。
229. 1.98版支持Servlet规范的Filter。
230. 1.98版支持Servlet规范的各种Listener。
231. 1.98版支持双向SSL认证。
232. 1.98版修改UFO probe的一个显示上的Bug。
233. 1.91版修改1.90版中引入的轻微内存泄漏和keepAliveConns的UFO Probe监测指标异常的Bug。
234. 1.90版提供英文版:examples英文版、UFO probe英文版和Howto文档英文版。
235. 1.90版修改一个不稳定因素。由于在1.70版中对"keep-alive"进行了标准的实现,改动比较大,1.70-1.88版本并不能保证“永远不宕”。
236. 1.90版在UFO Probe中增加keepAlive Connection数的指标监测。
237. 1.88版对体积小(<30KB)的静态网页的输出吞吐量接近100% Pure Java所能达到的最大值。
238. 1.87版将Probe采点从3000个下降为1500个,并修改Probe Client的一个数组越界错误。
239. 1.87版在bin目录下增加reload.bat和reload.sh,运行它们,可以不重新启动UFO,而能使对任何.xml配置(port, ssport参数除外)的修改、增删生效。
240. 1.85版修改不能读一些xml配置文件的Bug。
241. 1.85版对启动时的一些信息的提示更正确。
242. 1.85版在请求文件"Not Found"时,以绝对路径显示,查错更容易,UFO不用担心Hacker。
243. 1.83版完全支持将docBase和appbase设置成绝对路径。当它们以"/"或者"D:"开头,UFO就把它们判定为绝对路径。关于appBase的意义:如果配置了path=""的Context,appBase的唯一意义是放置war包的目录。
244. 1.81版修改UFO Probe的若干小问题。
245. 1.80版内置UFO Probe,监测UFO Web Server的重要运行数据,及时报警。
246. 1.71版在bin/UFO.bat, UFO.sh, UFOW.bat三个文件中增加JAVA_OPTS变量,让您方便地修改这个变量的值,以控制UFO进程的堆内存大小。关于JVM的堆内存的概念请查网上的相关文章。
247. 1.70版的评测指标应有进一步的提高,负载能力和响应速度等应有明显提高。
248. 1.70版对Http头信息里的"Connection: keep-alive"进行了标准的实现,从而支持一些Ajax网页的运行正确。
249. 1.70版对Cookie进行了改变,不再支持将Cookie的Value等直接设置为中文,而需要先进行转化,参见本发布的相关例子。
250. 1.70版预防了一种Hacker攻击的可能。
251. 1.70版对出错提示更友好。
252. 1.70版在exmaples Context里增加了Ajax栏目例子。并在这些Ajax例子对UFO唯一根据URL里的"/servlet/"字符串识别servlet的用法进行了足够的demo。(那种每增加一个servlet都需要配置一番的做法太麻烦了!)
253. 1.59版由UFO Web Server主程亲自校对整理增删修改examples Context,向您demo UFO Web Server精彩强大的功能。
254. 1.56版修改当response的头信息"Content-disposition"为"attachment;filename=xxx"时,response.getWriter()的一个Bug。
255. 1.55版修改与response.getOutputStream()相关的一个较严重的Bug。
256. 1.50版按照Jsp规范和Servlet规范,进行了全面的仔细的校对,修改了若干个不常用的地方,保持与Tomcat兼容。
257. 1.50版增加bin/startupW.bat,提供另外一种保险的把UFO作为Windows service启动办法。
258. 1.45版支持jsp规范的pageEncoding属性。
259. 1.45版将servlet的destroy()方法调用规范化。
260. 1.45版解决request的setCharacterEncoding(String env)方法不起作用的Bug。
261. 1.30版完全兼容Tomcat在web.xml对servlet进行配置的做法。
262. 1.30版对错误提示更标准。从而解决一些含有错误url连接的网页显示不出来或"Not Found"的问题。
263. 1.20版解决一些浏览器不标准规范含有一些固有的名字为Cookie保留关键字或字符的Cookie时,语句"Cookie[] cookies=req.getCookies()"不能返回Cookie数组,而是throw了例外的问题。1.20版UFO修改成:当浏览器含有一些固有的名字为Cookie保留关键字或字符的Cookie时或用户设置了名字为Cookie保留关键字或字符的Cookie时,语句"Cookie[] cookies=req.getCookies()"都返回Cookie数组,但不包含那些名字不规范的Cookie,只包含那些名字规范的Cookie,而不会throw例外。
264. 1.19版更好地支持特大文件的上传(100M以上的)。
265. 1.18版支持文件上传,支持UFOUpload、jspSmartUpload和Apache组织的commons-fileupload包。也就是很好地支持RFC 1867规范(参见http://www.ietf.org/rfc/rfc1867.txt)。这是一个很实用的功能。
266. 1.17版修改了如果jsp中用到了第三方类,这个第三方类的类名与JDK中的类名一样,并且UFO在编译这个jsp时正好用到了这个类名,导致UFO编译jsp失败的问题。
267. 1.16版修改了以前版本UFO用来搭建wap网站时,由于一些地方的无线网关不标准,手机用户需要访问两次http://wap.xxx.com,才能出内容的问题。也就是说,用1.16版本的UFO搭建wap网站,即使一些地方的无线网关不标准,手机用户也可顺利访问wap网站。
268. 1.15版修改了1.10版可能不能显示“验证码”图片的Bug。
269. 1.10版修改了以前版本中对新建的Context需要手工创建"_work"子目录的大Bug。
270. 1.10版修改了https状态下的多个Bug。
271. 1.05版修改了由于模糊打包导致1.00版中数据库连接池不能用的Bug。
272. 1.00版提供全新的demo的jsp和servlet示例,尤其对中文乱码问题的解决进行足够的demo。
273. 1.00版允许在一个UFO进程内配置多个DBConnectionPool,根据poolName访问。
274. 1.00版对servlet自动检查servlet.class是否更新,如果有更新,自动对UFO的servlet对象更新,而不需要重新启动UFO。
275. 0.999x版在测试150个jsp和servlet程序的基础上,修改了几个Bug,并提高各种“评测”指标。
276. 0.9985版在测试350个jsp和servlet程序的基础上,修改了几个Bug。
===================================================================================
server.xml篇
===================================================================================
<Connector port="81" scheme="http" web="on" webSocket="on"
connectionTimeout="20*1000" soTimeout="10*1000"
soRcvBuf="1024" soSndBuf="8192"
maxConnectionNum="5000000"
maxURISize="10*1024" maxHeaderTotalSize="10*1024" maxPostSize="2*1024*1024"
compression="off" compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"
noCompressionUserAgents="gozilla, traviata"
sendfile="off">
<Balancer URI="/, /index.html, /index.htm" AI="false" sessionKeepTime="0">
<Threshold CPU="20" freeThread="10" freeMemory="1*1024*1024" connNum="20*1000" request5="-1" flowUp5="-1" flowDown5="-1" session="-1"/>
<Member redirect="http://localhost:82" loadfactor="1" livePing="30*1000" deadPing="60*1000" pingURI="/index.html"/>
<Member redirect="http://127.0.0.1:83" loadfactor="2" livePing="30*1000" deadPing="60*1000" pingURI="/index.html"/>
</Balancer>
<Balancer URI="/games/*.jsp" AI="true" askTime="1*60*1000" sessionKeepTime="4*60*60*1000">
<Threshold CPU="0" freeThread="0" freeMemory="0" connNum="0" request5="-1" flowUp5="-1" flowDown5="-1" session="-1"/>
<Member redirect="http://127.0.0.1:8012" loadfactor="1" livePing="1*60*1000" deadPing="5*60*1000" pingURI="/test.html"/>
<Member redirect="http://222.73.205.120:80" loadfactor="2" livePing="1*60*1000" deadPing="5*60*1000" pingURI="/test.html"/>
<Member redirect="http://www.gm365bkp.com" loadfactor="1" livePing="1*60*1000" deadPing="5*60*1000" pingURI="/test.html"/>
</Balancer>
</Connector>
请解释scheme属性的含义。
当scheme为http时,UFO就启动一个普通的ServerSocket;当scheme为https时就启动一个https的ServerSocket。
当scheme为https时,您最好指定keystoreFile的存放位置。关于如何制作keystoreFile文件请按照/docs/https_Howto.txt所说的来做。
UFO可以支持多个<Connector>标签。
当web="on",本Connector处理响应普通的web请求(这里的普通是相对于WebSocket而言的);当web="off"时,本Connector不输出静态网页和动态网页,但仍然响应WebSocket的建立连接请求、并响应Probe的连接请求,并提供服务。
当webSocket="on"时,本Connector支持WebSocket的连接;webSocket="off",本Connector不支持WebSocket。
connectionTimeout: 单位ms, 是指UFO接收了连接请求到接收到至少一个byte数据的最大时间长度。
soTimeout: 单位ms, 当一个http连接处理了一次连接请求任务后,从输出数据完毕算起到下次接收到至少一个Byte数据的最大时间长度。soTimeout参数只对http连接有意义,对websocket连接无意义。
如果上面2个情况下的“最大时间长度”超出,并不是立即将这个连接Kill掉,而是在UFO执行检查时进行清理(UFO有一个线程每隔20分钟会执行一次检查,另外如果连接数达到最大时,也立即执行检查)。
soRcvBuf:单位Byte,UFO的连接的Socket的SO_RCVBUF参数,也就是接收缓存的大小。如果soRcvBuf<=0,表示不设置,采用系统默认缓存大小;如果soRcvBuf<512且soRcvBuf>0,则UFO修改成512;如果soRcvBuf>128*1024,则UFO修改成128*1024。
soSndBuf:单位Byte,UFO的连接的Socket的SO_SNDBUF参数,也就是输出缓存的大小。如果soSndBuf<=0,表示不设置,采用系统默认缓存大小;如果soSndBuf<512且soSndBuf>0,则UFO修改成512;如果soSndBuf>128*1024,则UFO修改成128*1024。
soRcvBuf和soSndBuf这2个参数的作用请见Socket的setReceiveBufferSize(int size)和setSendBufferSize(int size),把它们设置得较大,可能会提高下载速度,提高网页输出的流畅度,但也消耗更多的内存,减小了UFO的负载能力。
maxConnectionNum: 本Connector所能保持的最大连接数。
maxConnectionNum主要是用来防止UFO的所带的连接数过多而用光内存而死掉的。
当UFO的Connector所带的连接数达到maxConnectionNum时,UFO是怎样处理的呢?
这时,会把一些呆的时间长的http和https连接kill掉(但WebSocket连接和probe连接除外)。
maxURISize: 单位Byte,最大的URI长度。
maxHeaderSize: 单位Byte,最大的Http头体积(总和)。
maxParameterSize: 单位Byte, POST方法并且content-type为application/x-www-form-urlencoded或multipart/form-data时,POST的内容体作为Parameter保存在内存中的最大值。
Q3-7 打开/conf/server.xml文件,UFO的<Session>标签是这样的:
<Session maxInactiveInterval="30" maxMemNum="1000" maxNum="1000000000" />
请解释其各自属性的含义。
maxInactiveInterval:单位分钟,Session的最大不活动时间,当一个Session的不活动时间超过此数时,就Kill掉。
maxMemNum:单位个,保存内存中的Session的最大个数,Session个数超过此数,把多出的Session保存进内置的特制数据库。Session保存在内存里,可加快访问速度,但消耗内存。
maxNum:单位个,Session的最大数,保存在数据库里的Session数和内存中的Session数的和超过此值将把数据库里最早保存的1万个Session删除。本参数是用来防止硬盘被用光的(当硬盘被用光时,UFO必宕)。1万个Session需要400K硬盘来保存,请用户根据你的服务器硬盘大小来设置maxNum。
<Debug mode="0" />
请说明是什么意思。
当你采用UFO Web Server做开发时,要在Dos窗口下显示必要的调试信息,可设置本标签,你可以把本标签设置成:
<Debug mode="1" />
<Debug mode="2" />
<Debug mode="3" />
解释如下:
当mode为"0"时,是正常情况,不显示信息。
当mode为"1"时,显示UFO Web Server所接收到的所有连接请求的requestURI, queryString和method。
当mode为"2"时,除了显示mode为"1"时的信息外,还显示这个请求所对应的文件名以及它是否存在。
当mode为"3"时,除了显示mode为"2"时的信息外,还显示连接请求的所有的http头信息的内容。
<Log level="0" maxSize="200*1024" />
请说明是什么意思。
这是设置UFO的运行log的。
maxSize: 单位KB,log文件的最大体积。当log文件的体积超过此值时,就停止写log。
level: 写log的级别。
level="0": 0级,log文件只记录引起宕机的致命原因。
level="1": 1级,log文件还记录开发诊断信息,开发者遇到开发上的问题时,可采用此级log,然后将logs/目录下的log.txt文件发到gm365365@sina.com,我公司技术支持看log.txt就能知道问题所在。
level="2": 2级,暂时未实现。
level="3": 3级,暂时未实现。
注意:默认的log级别是0级!级别越高,写的信息就越多,牺牲的性能就越多,所以建议只在开发调试时采用1级或以上级!
UFO的log文件名为rlog.txt,在UFO安装目录下logs子目录里。
UFO在启动时,都会把上次的rlog.txt copy成rlog_old.txt文件。
<UFOThread name="RegUser" queSize="100" />
<UFOThread name="CChessUser" queSize="100" />
<UFOThread name="TomFee" queSize="5" />
请说明是什么意思。
这是配置Servlet异步处理的线程。关于异步处理方面的问题请阅读后面的<Servlet 3.0>的相关话题。
说明:如果你的网站没有用到Servlet的异步处理,请把这3个标签注释掉。否则,UFO启动时,会启动3个不用的线程,白白占用内存。
<Compiler name="JDT" />
<!--
<Compiler name="Sun" />
-->
请说明是什么意思。
这是选择UFO的Java编译器。
<Compiler name="JDT" />表示UFO的Java编译器采用eclipse的JDT Java编译器。
<Compiler name="Sun" />表示UFO的Java编译器采用Sun公司的Java编译器。
如果不设置Compiler标签,或者错误地配置Compiler标签的name属性,UFO都采用eclipse的JDT Java编译器。
<Probe maxConnections="10" perTime="1200" clientSet="on" />
请说明是什么意思。
这是配置UFO Probe.
maxConnections="10" 表示同时支持10个UFO Probe用户。
perTime="1200" 表示每隔1200秒采一个点。
clientSet="on" 表示UFO Probe可以通过Client随时设置UFO的perTime等参数。
<!--
<Valve type="RemoteAddr" deny="127.0.0.1" />
<Valve type="RemoteHost" allow="localhost" deny="www.*.net" />
-->
请说明是什么意思。
这是用来屏蔽一些用户(例如Hacker)访问网站的。
<Valve type="RemoteAddr" deny="127.0.0.1" />是根据用户的IP来屏蔽。
<Valve type="RemoteHost" allow="localhost" deny="www.*.net" />是根据用户的host来屏蔽。
这里deny意思好理解,就是拒绝后面用户访问。
但allow的理解要注意,如果配置了allow,表示只允许后面的那些用户访问,其它用户都将被拒绝。
关于allow和deny在多个IP或host的写法要符合正则表达式规定。
<Valve type="Host" match="No" />
请说明是什么意思。
本标签是用来防止一些Hacker的。
match="Yes":要求用户的URL严格地匹配UFO配置的某个Host,才给予服务。
match="No":不要求用户的URL严格地匹配UFO配置的某个Host,当不匹配任何Host时,就认为他是访问默认的Host。
关于本话题,请阅读下一问题。
在server.xml里有一个标签<Valve type="Host" match="No" />,当它的值match为"Yes"时,表示对虚拟主机进行严格匹配。就是,Host标签里未配的"host"都将得不到服务。
为了进一步理解这个问题的意义,在此啰嗦一下,国家有关部门规定:未备案的网站不得运行。这就带来了麻烦。为了明白这个规定是怎样带来麻烦的,让我给您讲个故事:
我公司的网站是www.gm365.com,它是备案了的,合法的,没有问题的。我公司的www.gm365.com网站运行在我公司托管的服务器上,服务器的IP是222.73.205.120,web server是UFO Web Server。
可是,有个无聊的Hacker注册一个域名xuoxing1.com,也指向了222.73.205.120,这样,当用户在浏览器里输入http://www.xuoxing1.com的时候,也访问到了www.gm365.com的主页。
但是,xuoxing1.com是一个未备案的网站,国家有关部门找麻烦来了,他们通过机房通知你,在你的服务器上运行着一个未备案的网站,你必须把它封掉,否则将如何如何。。。
于是,我公司立即将server.xml改成这样:
......
<Valve type="Host" match="Yes" />
<Host appBase="webapps" name="www.gm365.com">
<Alias>gm365.com</Alias>
<Alias>222.73.205.120</Alias>
<Alias>localhost</Alias>
<Alias>127.0.0.1</Alias>
<welcome-file>index.html</welcome-file>
<Context path="/examples" docBase="webapps/examples" >
</Context>
<Context path="/struts2_demo" docBase="webapps/struts2_demo" >
</Context>
<Context path="" docBase="webapps/ROOT" >
</Context>
</Host>
然后,再重启UFO Web Server。这样,gm365网站照样运行,而xuoxing1.com得到了屏蔽。
<Host appBase="webapps" name="www.gm365.com">
<Alias>gm365.com</Alias>
<Alias>222.73.205.120</Alias>
<Alias>localhost</Alias>
<Alias>127.0.0.1</Alias>
<welcome-file>index.html</welcome-file>
<Context path="/examples" docBase="webapps/examples" >
</Context>
<Context path="/struts2_demo" docBase="webapps/struts2_demo" >
</Context>
<Context path="" docBase="webapps/ROOT" >
</Context>
</Host>
<!--
<Host appBase="wapapps" name="wap.gm365.com">
<Context path="" docBase="wapapps/ROOT" >
</Context>
<welcome-file>index.wml</welcome-file>
</Host>
-->
请解释它们的含义。
第1个Host标签表示配置了一个名字为"www.gm365.com"的Host(虚拟主机),它有4个别名:
gm365.com
222.73.205.120
localhost
127.0.0.1
用户在浏览器输入如下字符串开头的URL时,都将访问到这个Host(虚拟主机):
http://www.gm365.com
http://gm365.com
http://222.73.205.120
http://localhost
http://127.0.0.1
www.gm365.com
gm365.com
222.73.205.120
localhost
127.0.0.1
这个Host(虚拟主机)的welcome file是index.html。它配置了3个Context。
第2个Host标签表示配置了一个名字为"wap.gm365.com"的Host(虚拟主机)。
用户在浏览器输入如下字符串开头的URL时,都将访问到这个Host(虚拟主机):
http://wap.gm365.com
wap.gm365.com
这个Host(虚拟主机)的welcome file也是index.html。它配置了1个Context。
server.xml里的<Valve type="Host" match="No" />,当它的值match="No"时,才可以有默认的虚拟主机。
第一个虚拟主机便是默认的虚拟主机。在/conf/server.wml中请您把要作为默认的虚拟主机放在最前面。“默认”的意思您明白吧,就是当一个URL找不到所属的虚拟主机的时候,就由默认的虚拟主机来处理。
这种情况下,您可以不必配置<Host>标签,把所有的html、jsp等所有的文件和子目录都要放在webapps/ROOT目录下。
如果您要灵活指定html等文件子目录存放位置,您就要在/conf/server.xml里配置<Host>标签,象下面这样:
<Host appBase="webapps" name="www.3ren.com">
<Context path="" docBase="webapps/dir1" >
</Context>
</Host>
是有需要注意的地方,就是UFO是根据URL的host参数来区分它属于哪个虚拟主机的。含义是:虽然localhost和127.0.0.1都指向了同一个地方,但是在网页制作里却不能随便替代。
什么是URL里的host参数?URL里以"http://"开始的位置到后面第一个"/"所夹着的字符串,为host。例如:
http://www.gm365.com/index.html, host为www.gm365.com
http://www.gm365.com:81/wap/index.wml host为www.gm365.com:81
http://www.gm365.com host为www.gm365.com
www.gm365.com/index.html host为www.gm365.com
group3.rnx.com host为group3.rnx.com
http://localhost host为localhost
127.0.0.1/index.wml host为127.0.0.1
简单一点说Context就是让您灵活地设置文档的目录,您知道,在Apache里,文档的目录为htdocs目录,但是在UFO里,有了Context的概念后,文档的目录就可以任意指定。Context是一个比虚拟主机更重要的概念。从字面上看,Context是上下文环境的意思,显然,Context是一组含义。其中最重要的一个含义是Context的识别字符串。在/conf/server.xml里有对一个<Context>的配置:
<Context path="/examples" docBase="webapps/examples" >
</Context>
这个path的理解很重要,它将决定一个URL属于那个Context。UFO对所接收到URL并不是单纯地理解成:(路径)+(文件名),而是理解成:(虚拟主机)+(Context)+(路径)+(文件名)。举一个例子,在浏览里输入:
http://www.gm365.com/examples/servlets/index.html
UFO如何理解这个URL呢?它首先根据www.gm365.com(也就是host,见前述),确定这是属于哪个虚拟主机的URL;接着,它把/examples理解成是这个虚拟主机下的一个Context;再接着,它把/servlets/index.html理解成这个Context下的servlets目录下的index.html文件。/servlets/index.html的父目录是什么呢?就是在/conf/server.xml里为/examples这个Context配置的docBase,也就是webapps/examples。
Context除了上面所说的识别字符串和docBase两个概念外,还有:可以配置Context的mime的extension与mime-type的对应关系,以及配置Context的welcome-file。
<Context path="/examples" docBase="webapps/examples" >
</Context>
<Context path="/examples/example" docBase="webapps/examples" >
</Context>
然后,对URL:
http://www.gm365.com/examples/example/test/index.html
UFO是如何理解的?最后是输出webapps/examples/test/index.html还是输出了webapps/examples/example
/test/index.html?
UFO是按照“最长匹配”来寻找Context的,对于上面的URL,UFO寻找Context的步骤如下:
第一步:把host部分去掉,得到一个字符串:/examples/example/test/index.html;
第二步:把文件名部分去掉,得到字符串:/examples/example/test,并寻找是否有path值等于这个字符串的Context,结果发现没有;
第三步:把上面字符串:/examples/example/test的最后一个目录名去掉,得到字符串:/examples/example,并寻找是否有path值等于这个字符串的Context,结果找到了!
因此,对您这个URL,UFO的理解是请求"/examples/example"这个Context下的/test/index.html文件。由于"/examples/example"这个Context的docBase是"webapps/examples",因此最后输出了webapps/examples/test/index.html。
<Context path="" docBase="webapps/ROOT" >
</Context>
我的理解是否正确?
您的悟性真高。您迟早会成为用UFO做网站的高手的。不过,UFO从0.999版开始,如果发现您没有配置这样的一个Context,会在启动的时候,自动为您加上这样的一个Context。
<Context path="" docBase="D:/test/Docs" >
</Context>
在UFO里,Context的docBase可以设置为绝对路径也可以为相对路径。
war包只能放在server.xml文件里Host标签的appBase所指定的目录下。例如:
<Host appBase="webapps" name="www.gm365.com">
.....
那么要求您把所有的war包都放在webapps目录下。
appBase的意义有两点:
1. 是放置war包的目录;
2. UFO在启动时,会检查appBase下的每个子目录,如果发现server.xml里没有一个配置的Context的path是"/"+目录名,那么UFO会自动加上这样一个Context,它的path是"/"+目录名,它的docBase指向这个子目录。但是有一个例外!就是UFO始终把ROOT子目录的所对应的Context的path设置成"/"。
<Balancer URI="/, index.html, index.htm" HostName="www.gm365.com">
<Member redirect="http://222.73.205.120" loadfactor="2" />
<Member redirect="http://www.gm365.com" loadfactor="1" />
<Threshold thread="5" CPU="7" request5="1000" flow5="50*1024*1024" session="100000" keepalive="2000" memory="250*1024*1024" />
</Balancer>
请解释它们的含义。
本标签配置了一个负载均衡。本标签表示把Host name为"www.gm365.com"的虚拟主机的/或index.html或index.htm的请求在满足一定的阀值条件时按权重因子重定向到另外的服务器。
URI: 要重定向的URL里的http://(或https://)后的第1个/后面的部分。它可以是一个请求;也可以把多个请求放在一起而用","或", "分割。
HostName: Host的name。它可以是具体的一个Host的name;也可以是"*",表示任何Host。它不可以把多个Host的name放在一起。
Member: 定义了一个负载均衡的服务器。
redirect: 将重定向到的另外服务器的协议、IP和端口。
loadfactor: 权重。UFO将根据请求次数按权重把URI里规定的请求重新定向。
Threshold: 定义了重新定向的阀值,只有阀值条件满足时才进行重新定向,否则仍然由当前的服务器来服务。它有memory等7个属性,这7个属性之间是或的关系而不是与的关系,也就是说,任何一个属性条件满足时,都会发生重新定向。
thread: 在用线程数。当UFO进程的当前正在工作的线程数>=thread属性约定的值时就重新定向。注意:thread="-1",表示不考虑在用线程数因素!thread="0",表示无条件重新定向!
CPU: 当前CPU,单位%。当UFO进程的当前占用的CPU>=CPU属性约定的值时就重新定向。注意:CPU="-1",表示不考虑当前CPU因素!CPU="0",表示无条件重新定向!
request5: 最近5秒内的请求次数。当UFO进程的最近5秒处理的请求数>=request5属性约定的值时就重新定向。注意:request5="-1", 表示不考虑request5因素!request5="0",表示无条件重新定向!
flow5: 最近5秒内的流量(上行+下行),单位Byte。当UFO进程的最近5秒流量>=flow5属性约定的值时就重新定向。注意: flow5="-1",表示不考虑流量因素!flow5="0",表示无条件重新定向!
session: 当前Session数。当UFO进程的当前的session数>=session属性约定的值时就重新定向。注意:session="-1",表示不考虑session数因素!session="0",表示无条件重新定向!
keepalive: 当前keepalive连接数。当UFO进程的当前的keepalive连接数>=keepalive属性约定的值时就重新定向。注意: keepalive="-1",表示不考虑keepalive连接数因素!keepalive="0",表示无条件重新定向!
memory: 在用内存,单位Byte。当UFO进程的当前使用内存>=memory属性约定的值时就重新定向。注意:memory="-1",表示不考虑在用内存因素!memory="0",表示无条件重新定向!
<Balancer URI="games/login.jsp" HostName="*">
<Member redirect="http://127.0.0.1:8012" loadfactor="1" />
<Threshold thread="-1" CPU="-1" request5="-1" flow5="-1" session="-1" keepalive="-1" memory="0" />
</Balancer>
请大体说说这个负载均衡。
UFO收到的任何虚拟主机的games/login.jsp的请求都无条件地重新定向到http://127.0.0.1:8012。
<DBConnectionPool
poolName="gm365DBSZJ"
driver="org.gjt.mm.mysql.Driver"
username="gm"
password="112358"
URL="jdbc:mysql://61.151.252.98/gm365?user=gm&password=112358&useUnicode=true&characterEncoding=gb2312"
minOpen="1"
maxOpen="100"
timeout="2000"
/>
请解释它们的含义。
本标签配置了一个name为"gm365DBSZJ"的数据库连接池。
预先创建好数据库连接,可以提高jsp程序的响应速度;可以避免随意创建数据库连接而“打爆”数据库(数据库能带的连接数是有限的)。
但是在你配置了这样的一个数据库连接池后,你怎么在servlet或jsp里使用它呢?
第一步,把相应的jdbc driver的.zip包或.jar包放到/lib目录下。(重启UFO,当然)
第二步,编写jsp程序,参见examples/jsp/DB/DBJsp.jsp程序:
<%@ page contentType="text/html; charset=gb2312" %>
<%@ page import="java.sql.*"%>
<%@ page import="javax.db.DBConnectionPool"%>
<%@ page import="core.UFO"%>
<html>
<body>
以下是从MySQL数据库读取的数据:<hr>
<table border=1>
<tr><td>ID</td><td>书名</td><td>出版社</td><td>价格</td></tr>
<%
DBConnectionPool dbConnPool=UFO.getDBConnectionPool("gm365DB");
java.sql.Connection conn=null;
java.sql.Statement stmt=null;
java.sql.ResultSet rs=null;
try{
conn=dbConnPool.getConnection();
stmt=conn.createStatement();
rs=stmt.executeQuery("select * from book");
while (rs.next()){
out.println("<tr>");
out.println("<td>"+rs.getString("bookId")+"</td>");
out.println("<td>"+rs.getString("bookName")+"</td>");
out.println("<td>"+rs.getString("publisher")+"</td>");
out.println("<td>"+rs.getFloat("price")+"</td>");
out.println("</tr>");
}
}
catch (Throwable e){
}
if (rs!=null){
try{
rs.close();
}
catch (Throwable e){
}
}
if (stmt!=null){
try{
stmt.close();
}
catch (Throwable e){
}
}
dbConnPool.returnToPool(conn);
%>
</table>
</body>
</html>
当到数据库连接池取连接时,如果没有空闲的连接,并且连接池打开的连接数已经达到了最大数,这时如果立即返回null,就有问题了,因为再过几ms就可能有一个连接返回池中;如果无限时间地等待下去,也有问题,因为有可能永远也没有连接返回池中,导致调用线程“吊死”。timeout属性就是用来解决这个问题的,等待timeout ms,若还没有连接可取,则throw Exception。
程序本身是最精确的文档,请参见DBConnectionPool.java
如果您在/conf/server.xml配置正确的<DBConnectionPool>标签,并且数据库确实已经启动,当UFO启动时,就已经创建了数据库连接池。以后您只要在jsp程序里取出DBConnectionPool的引用就可以了,为此您需要:
1. 在jsp程序的一开头写上:
<%@ page import="javax.db.DBConnectionPool"%>
<%@ page import="core.UFO"%>
2. 在jsp程序里取出UFO的DBConnectionPool的引用:
DBConnectionPool dbConnPool=UFO.getDBConnectionPool("gm365DB");
这里的"gm365DB"是poolName,要在配置文件里配置过的。
3. 从数据库连接池中取连接:
java.sql.Connection conn=dbConnPool.getConnection();
以后的做法,都是正常的数据库的jdbc操作。
dbConnPool.returnToPool(conn);
请问这语句是否是必须的?
必须!从数据库连接池中取出连接后,用完后,一定要调用dbConnPool.returnToPool(conn)或dbConnPool.wipe(conn)中的一个,即使发现这个连接已死,也要这么做!否则,数据库连接池关于连接数的数据就不对了,导致您的网站宕掉!
请参见DBConnectionPool.java
<DBConnectionPool>标签可以在两种文件里配置:
1. 在/conf/server.xml文件里配置,可以配置多个<DBConnectionPool>标签,请不要将
<DBConnectionPool>标签嵌在<Host>标签里面(目前不支持这种做法);
2. 可以在各个Context里的web.xml里配置多个<DBConnectionPool>标签,但是不能在/conf/web.xml
里配置<DBConnectionPool>标签。
特别注意的是:无论在什么地方配置,配置多少个<DBConnectionPool>,请保持poolName属性的唯一性。
是这样的。这是因为UFO启动时装载JDBC Driver类的结果。因此,对于那些不用数据库连接的网站,请您在/conf/server.xml里把<DBConnectionPool>标签注释掉,就没有这个问题了。
<GlobalNamingResources>
<Resource
name="jdbc/myDB"
type="javax.sql.DataSource"
driverClassName="org.gjt.mm.mysql.Driver"
username="mars"
password="234235"
url="jdbc:mysql://61.151.252.98/gm365?useUnicode=true&characterEncoding=gb2312"
minOpen="1"
maxOpen="10"
maxWait="2000"
maxIdle="2"
maxActive="4" />
</GlobalNamingResources>
请解释它们的含义。
这是按照DataSource的相关规范配置了一个数据库连接池。这个数据库连接池按照DataSource的标准方式来访问。
UFO同时支持<DBConnectionPool>和<Resource>两种数据库连接池的配置方法。两种方法配置出来的数据库连接池在UFO Probe看来没有差别。
建议您采用<DBConnectionPool>配置方式,UFO支持<Resource>的配置方式主要是为了对Tomcat等其它Web Server的兼容。
有关数据库连接池、DataSource、JNDI部分的所有源代码在javax.ufo.db目录下。
===================================================================================
web.xml篇
===================================================================================
<mime-mapping>
<extension>abs</extension>
<mime-type>audio/x-mpeg</mime-type>
</mime-mapping>
它们是么意思?
上面的例子标签的意思是,如果用户请求下载文件的扩展名是.abs,就将输出的Http头里的Content-Type填写为audio/x-mpeg。
这个mime-mapping功能在当今日新月异的互联网(尤其是无线互联网)比较重要。例如,对Android手机,如何发布游戏呢?
你需要在/conf/web.xml里加上如下标签:
<mime-mapping>
<extension>apk</extension>
<mime-type>application/vnd.android.package-archive</mime-type>
</mime-mapping>
这样当android手机用户到你的网站来下载.apk游戏时,android手机得到了"application/vnd.android.package-archive"的Content-Type的Http头,手机知道了这是一个要安装的包。否则手机会把游戏包当网页显示成乱码了。
不混乱。你应该知道面向对象的“继承”和“重载”的关系吧。在/conf/web.xml里对mime的extension与mime-type对应关系进行配置,各个Context都拥有这些配置。但是,个别Context要对某一个mime的extension所对应的mime-type定义新的值,怎么办?可以在那个Context的/WEB-INF/目录下的web.xml对那个extension与那个mime-type重新对应。
是这样的。与mime参数相比,多了一个配置的地方:/conf/server.xml。
各个文件配置的welcome-file的“继承”和“重载”的关系是:/conf/web.xml配置的welcome参数在所有虚拟主机的各个Context下都得到“继承”;/conf/server.xml里配置的welcome参数对所在虚拟主机的各个Context的welcome参数进行“重载”;每个Context的/WEB-INF/目录下的web.xml文件对welcome参数的配置,对自己的welcome参数进行“重载”。
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>HelloUFO</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloUFO</url-pattern>
</servlet-mapping>
请解释它们的含义。
这两个标签规定了一个Servlet,它们规定用户请求访问到本"examples"的Context的URL里"/HelloUFO"字符串看成是访问一个名为"HelloServlet"的Servlet,它的类是classes子目录下的HelloUFO.class.
<servlet>
<servlet-name>RequestDetail</servlet-name>
<servlet-class>RequestDetail</servlet-class>
<url-pattern>/RequestDetail</url-pattern>
</servlet>
请问这种写法与Q4-4里的写法效果有不同吗?
没有差别。在Q4-4里将同一个Servlet的规定放在2个标签里写,真是差的做法。那么多servlet要校对2次,真是费神的体力活,使人要疯掉了。
<security-constraint>
<display-name>Example Security Constraint</display-name>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<!-- Define the context-relative URL(s) to be protected -->
<url-pattern>/jsps/security/protected/*</url-pattern>
<!-- If you list http methods, only those methods are protected -->
<http-method>DELETE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<!-- Anyone with one of the listed roles may access this area -->
<role-name>ufo</role-name>
<role-name>role1</role-name>
</auth-constraint>
</security-constraint>
<!-- Default login configuration uses form-based authentication -->
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Example Form-Based Authentication Area</realm-name>
<form-login-config>
<form-max-login-num>200</form-max-login-num>
<form-login-page>/jsps/security/protected/login.jsp</form-login-page>
<form-error-page>/jsps/security/protected/error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- Security roles referenced by this web application -->
<security-role>
<role-name>role1</role-name>
</security-role>
<security-role>
<role-name>ufo</role-name>
</security-role>
这组标签规定了一个认证。它们规定用户访问到examples这个Context的URL只要匹配"/jsps/security/protected/*"就都要登录认证,DELETE, GET, POST, PUT访问都要登录认证。认证的方法是"FORM",登录界面是/jsps/security/protected/login.jsp,登录不对就转向/jsps/security/protected/error.jsp。登录填写的用户名要求拥有"ufo" 或"role1"角色。
<form-max-login-num>200</form-max-login-num>
请问这个标签是什么意思?
当UFO要求用户做FORM认证时,会把刚才的请求状态保存下来,等用户认证成功后,再把刚才保存的请求状态取出来,为用户服务。
但是内存里总不能保存任意多的请求状态。
当内存里保存的请求状态超过上面标签规定的数目时,就按照合理的机制把一些保存的请求状态删除。
role就是角色的意思。role是与权限密切相关的,UFO里的role的意思与普通系统里的role没有差别。设置某个权限,要填写一堆用户名,没有什么;如果每次设置其它权限,都要填写一堆用户名,那就很麻烦了。有了role,就解决了这个问题,将权限授于角色,而把用户名设置成是否拥有某个角色。
一个角色可以拥有多个用户名(相当于一个权利授于多个用户名),而一个用户名可以拥有多个角色(相当于一个用户名拥有多个权利)。
在/conf/UFO-users.xml里。例如:
<user name="ufo" password="ufo" roles="ufo" />
表示用户名"ufo"的password是"ufo",拥有的role是"ufo"。
Web Server上的一些网页、jsp程序、servlet程序、文件download等资源需要用户输入密码才能访问,这就是认证。
UFO支持BASIC、DIGEST和FORM等三种方式。
BASIC认证:浏览器弹出默认的登陆框,让用户输入用户名和密码,如果有一个不对,默认的登陆框继续弹出,直到输入正确为止。在传输用户名和密码上,只用简单的加密,Hacker截获字节流后,将轻易破译。
FORM认证:登陆框可以自己设置成自己的风格,用户名或密码输错后,出现自己定制的页面。在传输用户名和密码上,用POST方法的参数编码方式,Hacker截获字节流后,也将轻易破译。
DIGEST认证:在界面表现上与BASIC认证无异。在传输用户名和密码上,采用复杂Hash加密,Hacker截获字节流后,将很难破译。
不可以。这个四个字符串不能修改的,是FORM认证登陆的四个“关键词”。简单地说,您如果要制作其它风格的登陆框,可以修改上面login.jsp的界面部分,却不可以修改其中的“程序”部分。
===================================================================================
提问篇
===================================================================================
从4.00版开始,UFO不再对Tomcat保持完全兼容,但尽量保持兼容。
不兼容的地方主要在异步处理等新增规范标准的实现上。
UFO采用了非常优秀的对象管理机制和算法,UFO的JVM极少进行垃圾回收。用UFO运行网站,绝大多数时间CPU消耗<0.1%,最多的时候也不会超过5%,几乎永远保持很快的响应速度和高负载能力。
您只需要把Tomcat的webapps目录和conf目录Copy覆盖UFO的webapps目录和conf目录,然后启动UFO就可以了。
虽然UFO的/conf/server.xml文件里不需要<Engine>、<Service>、<Server>三个标签,但是对Tomcat的server.xml文件格式,UFO也完全支持读取。不过从简洁的立场出发,建议您还是参照UFO(发布)里的/conf/server.xml的样子把Tomcat的server.xml改过来(主要是去掉<Engine>、<Service>、<Server>三个标签)。
对一些特殊的网站,从Tomcat迁移到UFO上,您还需要做如下一些事情(一般情况您不需要理这些):
1. 如果您的网站用到了认证,请把tomcat-users.xml重命名为UFO-users.xml。
2. 如果您的网站用到了Servlet 3.0的异步处理,请参考UFO的例子做法修改过来。
String username=request.getParameter("user_name");
结果username在网页中显示为乱码。
如果您设置了request的characterEncoding,request.getParameter()就按您设置过的字符编码返回String;否则,按照ISO-8859-1字符编码返回String。在ISO-8859-1字符编码里,中文字符显示为乱码。
对此中文乱码问题,有两种解决办法:
1. 先设置request的characterEncoding,再取参数。即:
request.setCharacterEncoding("UTF-8");
String username=request.getParameter("user_name");
2. 不设置request的characterEncoding,自己转换(推荐)。转换办法如下:
String oldName=request.getParameter("user_name");
byte[] bs=oldName.getBytes("ISO-8859-1");
String username=new String(bs, "UTF-8");
特别注意的是,您要确保浏览器端发送这些参数的html网页的编码与Server端取这些参数的编码的一致。
例如,上面的方法中采用"UTF-8"来取参数,那么,对应的发送参数的网页应该是这样的:
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>XXX</title>
</head>
(当然,所有其它地方也都是UTF-8,即html文件保存的编码方式为UTF-8;html文件里其它如果出现GB2312等编码都把它们换成UTF-8)
使用UFO做Web Server的好处是只简单地管理一个UFO Web Server(而不需要管理配置其它模块), 网站能做得很稳定,永远也不会自己宕掉;UFO在托管机房Ping的TimeOut比率很高、遭受Hacker攻击、高负载压力请求、互联网骨干网被黑等恶劣的环境条件下仍然能很好地运行;UFO在对付Hacker方面也有足够措施。
将网站做得很稳定是非常重要的:
1. 将网站做得很稳定,就可以天天睡大觉也收钱;相反网站不稳定,公司上下经常受到折腾。
2. 网站容易宕,用户、业务就发展不起来;每宕一次会有可观的用户倒向竞争对手。
3. 对于一个要宕的网站,绝大多数情况是Web Server有垃圾资源的积累,垃圾的积累会使网站的响应速度变慢。因此,对于一个不是很稳定的网站,最大的坏处是它没有宕的时候的响应速度的变慢,研究表明每慢0.5s,就有20%的用户流失。
4. Web Server过多的垃圾积累,会导致整个Server计算机的变慢,导致别的Server程序响应变慢。
5. 网站现在越来越成为公司门面,公司网站不稳定或响应速度慢,公司形象受损。
6. 网站做不稳定,老板就容易失去信心,公司关门;做单子则不能干净利索地交活;技术人员就容易被炒鱿鱼。
另外,UFO自带很Cool的UFO Probe,对UFO Web Server的运行状况提供实时监测、诊断和报警。
一个充分条件:如果一个Server程序(例如,Web Server)在启动后,经过一段时间的运行(一般<7天),内存不能稳定下来,一直在增长,那么这个Server迟早要自己宕掉。
相反,如果一个Server程序经过一段时间的运行,内存能稳定下来,CPU峰值和响应速度都很正常,那么这个Server程序是一个很稳定的Server。
将问题发到信箱:gm365365@sina.com
作为开发者,我们不便做出这样的评测报告,无论是数据好坏,由开发者给出评测报告总会带来道德甚至法律上的问题。
您可以自己下载JMeter等测试工具自己来评测。
您的说法不正确。“猫走不走直线,完全取决于老鼠”。php速度快不快完全取决于运行它的Web Server。类似的错误说法还有:看,利用Google的python做的网站速度有多快。php, asp, jsp, python只是产生动态网页的语法。
实际上,我公司正准备使UFO也支持php,asp,以使那些利用php, asp开发的网站只需要简单Copy一下,就可以迁移到UFO下,但是最后还是放弃了。UFO支持php, asp不难,但是好象没有什么意义。
首先,我们看不出来php, asp, jsp在绝对大多数地方语法上有什么本质上区别,只是改头换面而言,例如,php的echo对应jsp的print等等。这就是说,网站如果想从php迁移到jsp上,利用jsp把动态网页部分重写一遍,是个不费脑筋的事情,只是花点时间干点体力活而已。
其次,我看不出来一些语言有什么前途,例如,一些语言连数据库连接池都没有,这非常致命;再例如,一些语言虽然提供了不少function,但是与jsp的在网页里编程相比是小巫见大巫了;再例如,一些语言是弱类型,这很糟糕。一些语言流行只能说明以前还没有出现稳定运行jsp的Web Server。
但是,现在UFO Web Server出现了,情况就不同了。
===================================================================================
付费篇
===================================================================================
UFO是免费使用的。但对免费使用的用户:
1.只能用mail提问,我公司也不一定立即回答;不能用电话联系或微信即时联系。
2.更别说我公司上门服务;
3.更别说我公司对您整个系统构架提供帮助(查找整个系统不稳定的原因、给出好的方案等);
4.豪华安装版Probe(能设置背景图片的)只有3个月的免费使用时间,但未来我公司会提供一个Applet风格(界面简陋且功能少)的Probe软件(和它对应的安装版)永久给用户免费使用。
用户要想得到上面更好更多的服务,我公司要收取不超过45万元的费用,主要按IP来收取,根据具体情况面谈确定。
===================================================================================
Servlet 3.0篇
===================================================================================
支持。参见UFO发布的examples下的一个Servlet例子。
当然,UFO也支持以前的通过UFOUpload实现文件上传的做法。两种做法相比,无论从方便和性能等方面比较,没有大的差别,但以前的UFOUpload文件上传的做法,可以屏蔽掉一些明显错误的文件类型。(这个比较重要,例如,上传照片,不应该是.txt, .doc文件吧)
目前不支持。
通过注解来声明,比起web.xml里配置一大堆标签,的确省事轻松了许多。
但是这种做法的坏处也非常明显:
1. Web Server的启动速度慢了,启动时要把每个Servlet的.class文件打开,扫描分析一遍。
2. Web Server要装进一堆注解分析的支持类,多消耗内存。
3. 与<Servlet>标签声明的Servlet完全一样,用户的URL要对一堆url-pattern做匹配甚至*匹配,连静止态网页的下载也还是要这么白浪费CPU。UFO提倡通过URL里的"/servlet/"字符串识别servlet的做法。(见<Q4-6>---<Q4-9>)
目前不支持。
有这么开发网站的么,咱没见过。
前面的Servlet 3.0的问题,都是可以用别的方法替代解决的,只是提供了另外一个选择,至多只是提供了方便,每个问题至多值50元钱,加起来也不过150元钱。
但是,这个话题所反映的问题价值几万至千万元。因为它可以将好几台Web Server的网站降为1-2台Web Server;将需要1000台Web Server的网站降为几台Web Server。
线程的开销是昂贵的,一个Web Server一般也就200个线程。无阻塞情况下,一个线程1s可以完成几百次请求响应,而象数据库操作、跨网络调用这样的阻塞等待时间一般在1s量级,有时是几分钟、几十分钟甚至更长时间。可见线程阻塞带来的负载能力损失时是很可观的,运气不好的时候,Web Server里的所有线程都处在阻塞等待状态,这样导致Web Server假死,网站瘫痪了。
我以为,这样根本没有解决问题,而且还设置了一个大陷阱:使自己的网站真正宕掉。
之所以“Servlet的异步处理”问题很有价值,是因为线程的开销昂贵,而且一个Web Server进程里能容许的线程数是有限的。把任务交给另外一个线程去干,并没有解决在阻塞等待时消耗线程的症结。而且,让用户自己去启动新的线程,很容易是Web Server进程里的线程数不可控,导致内存用光,Web Server真正宕掉。
让我给你讲一个类比:
你是一个公司的老板(Web Server),手下有200名员工(200个线程),你公司的业务是替客户到车站购买火车票。国庆节长要到了,要你公司购买火车票的客户很多,结果你的200员工都接到了任务,都到火车站的窗口排队买票了。国庆节前排队买票的人很多,结果你的员工都要排很长时间的队才能买到一张车票。你这下着急了:员工们都去排队了,这还接不接活了?
有一个高人(Servlet 3.0)看到了问题的严重程度,向你献策:你何不雇佣民工去火车站排队买票,而让员工继续接客户的活。
你一听,恩,好象这主意不错。你于是向你的员工下令:你们每个人接到客户的买票单子后,就自己去找一个民工(自己启动一个线程)替你到火车站去排队,民工的工资(内存开销)按时间由我支付。你们自己要不停去接客户的买票单子。
结果,你发现,民工的工钱一点也不便宜,你公司的买票单子接了很多,民工也请了很多。最后你付不起工资(内存爆了),你公司破产了(Web Server宕了)。
对于上面的类比,UFO的办法是:你去新招一名办事效率高的员工(优先级最高的线程)去窗口排队,你公司200名员工接的所有客户的买票单子都交给他去排队买票。
UFO的“Servlet异步处理支持”的类都放在了javax.ufo.async目录下。
你的需要做异步处理的Servlet该如何做呢?你一共要做2件事情:
一. 把你的Servlet写上implements UFOTask, 并把原先service()方法里的东东,全部原封不动挪到go()方法里。
二. 在service()里写4行代码:
1. 取出UFO Web Server的UFOThread:
UFOThread worker=UFO.getUFOThread("TomFee");
2. 取出HttpServletRequest的AsyncContext:
final AsyncContext actx=request.startAsync();
3. 创建一个UFOTaskEntry:
UFOTaskEntry myTask=new UFOTaskEntry(this, actx, request, response);
4. 把这个UFOTaskEntry加到UFOThread的队列里:
worker.addQueTask(myTask);
这个名叫"TomFee"的UFOThread是在/conf/server.xml里配置的:
<UFOThread name="TomFee" queSize="5" />
更精确的描述请见UFO发布的examples里的Servlet例子。
import javax.ufo.async.*;
import core.UFO;
找不到它们。
javax.ufo包在/lib/javax-ufo.zip里,core.UFO类在/bin/core.jar里。请设置classpath。
是的,jsp没有异步机制。
www.gm365.com的登陆和注册就是在jsp里连数据库的,我们准备把那两个jsp改成异步Servlet。
不过,目前www.gm365.com访问量不大,在jsp里阻塞等待对网站影响不大。但是作为一个技术上的完全的解决方案,要改!(你总是要假设网站有一天会火爆的吧)。
===================================================================================
WebSocket篇
==================================================================================
WebSocket就是建立在Web Server里的Socket。
普通的Socket联网的时候,是这么做的:
Socket sk=new Socket("www.gm365.com", 3950);
OutputStream ou=sk.getOutputStream();
InputStream in=sk.getInputStream();
这样您就可以利用这个Socket干活了,进行收发数据。
而WebSocket联网的时候,是这么做的:
Socket sk=new Socket("www.gm365.com", 80);
OutputStream ou=sk.getOutputStream();
String uri="/examples/websocket/CS/mine";
ou.write(("GET "+uri+" HTTP/1.1\r\n\r\n").getBytes());
ou.flush();
InputStream in=sk.getInputStream();
NetTool.skipHttpHeaders(in);
之后,您就可以利用这个Socket干活了,进行收发数据。就与普通的Socket无任何差别了。
可以看出,WebSocket建立后,要先告诉服务器,您是请求哪个Servlet服务;然后打开InputStream后,利用javax.ufo.ws.NetTool跳过Http头。之后,就可以象一个普通的Socket那样使用了。
关于本话题,请参见UFO Web Server发布的examples这个Context里的websocket例子。
WebSocket是为了解决以前浏览器端对Web Server的长连接太消耗Web Server的资源而推出的。
关于BS模式和CS模式的话题,我不知道是否这是个公认的说法,反正UFO Web Server采用了这种说法。
对于浏览器连接到Web Server的Socket,称为BS模式的Socket。
对于应用程序连接到Web Server的Socket,称为CS模式的Socket。
两种模式的WebSocket在处理上其实没太大的差别,只是BS模式的WebSocket对Http头检查严格些。
Q8-3 在web.xml里配置WebSocket的Servlet与配置普通的Servlet有没有不同?
无论是BS模式WebSocket的Servlet还是CS模式WebSocket的Servlet,在web.xml里配置起来与普通的Servlet没有任何差别。
支持BS模式WebSocket的Servlet要且必须要extends javax.ufo.ws.BWSServlet;支持CS模式WebSocket的Servlet要且必须要extends javax.ufo.ws.CWSServlet。UFO Web Server通过识别一个Servlet的父类是否是javax.ufo.ws.BWSServlet或javax.ufo.ws.CWSServlet,来将它确定为支持WebSocket的Servlet。
用UFO Web Server开发网游等联网应用,有2大好处:
1. 负载很高。比您采用普通的Socket(尽管采用NIO)不做技术性的处理所开发出来的应用Server的负载高出至少100倍应该不成问题。
2. 服务器体系稳定运行。这种稳定性除了UFO本身很稳定外,还在于在适当的条件下,我公司还为您提供阅读代码服务,为您精准地找出解决您的Servlet里影响稳定性的代码和做法。(本点参见后面相关话题)。
恩。1年前,被一个曾在多家知名IT公司做过云计算的资深老总问及这个问题。当时,我对这个问题其实很清楚,但是,这个问题却不能用几句话回答清楚,因为一台服务器能带多少人同时在线要取决于多个条件,所以当时我只能吱吱吾吾地回答。
1. 对于视频播放这种很占带宽的应用,带宽是瓶颈,对于这种应用,一台服务器应该是支持不了多少人同时在线。这种应用的服务器负载情况应该是:(平均)一个用户所占带宽有多小,服务器负载就能有多大。
2. 对于那些很消耗CPU的应用,例如,一些集中处理的计算,CPU是瓶颈,对于这种应用,(平均)一个用户所占CPU有多小,服务器负载就能有多大。
以上2种应用,探讨服务的负载问题几乎没什么意义,因为用户所占的带宽、CPU,你几乎无法改变。
但是对于90%的其它情形,用户访问服务器所占用的带宽和CPU其实是很微小的。典型地,对于我们平时访问网站这种情况,表面上看每张网页、图片体积很大,但是用户只是在第1次访问这个网页、图片时,服务器才把整个文件传送给Client,以后当该用户再次访问时,服务器只发送一个"304 Not Modified"的Http头,不过100个左右的Byte,让Client端从自己的缓存里取。因此,对于我们平时访问网站这种情况,服务器的负载其实可能做得很大。
而对于网游等应用,一般的消息只有1-几十个Byte,而且2次消息之间往往间隔几秒-几十秒,对于服务器来说,这是极轻、极稀疏的任务。就是说,从带宽和CPU的因素看,一台服务器估计都可能可以满足全球的人同时访问了。
但是,还有其它不少因素制约着服务器的负载能力,例如:没那么多线程、阻塞、内存不够等。对这些因素的处理就决定了服务器的负载能力,处理的技术水平不一样,服务器的负载能力就大不一样。
因此,如果您问我“一台服务器能带多少人同时在线?”,我没法回答您。您或许应该问:就这个应用,你来做服务器,一台服务器能带多少人同时在线?
一台普通的PC服务器应该至少能带1百万人同时在线。
www.gm365.com的早期的联网游戏是采用普通的Socket的BIO方式开发的,BIO方式下服务端的每一个Socket都要采用一个线程来监视(有没有数据到来),当一台PC服务器达到300多人同时在线时,服务器的CPU峰值经常在10%以上,服务器反应迟钝。
因此采用BIO方式的Socket开发的一台PC网游服务器负载也就是300-500人同时在线。其原因是一台计算机只能有1000个左右的线程,线程多了,线程本身调度的开销很大。
后来,www.gm365.com的联网游戏采用NIO方式的Socket开发,NIO方式下,服务器采用一个线程监视所有的Socket(是否有数据到来)。负载能力明显提高,从CPU峰值推算,一台PC网游服务器负载应该3000-5000人同时在线。
但是,(在采用NIO后)还有其它的瓶颈在制约着负载能力,负载能力还可以大幅度提高。
UFO的WebSocket正是这些实践的成果。UFO的WebSocket解决了NIO以外的瓶颈问题。
采用UFO的WebSocket开发的网游等联网应用,单台PC Server的负载能力,可以这么算:(平均)一个用户所占用的服务器内存有多小,服务器的负载就有多大。
UFO发布里自带的“喀秋莎-联网扫雷”的一个在线用户占用的服务器内存是3K(平均),现在一台普通的PC服务器内存是32G,这样单台普通的服务器运行这个联网扫雷,能支持的同时在线用户应该在1千万人。但是,这毕竟没有实践测试过(没有那么多用户),可能随着实际运行的同时在线用户数的增加还可能会出现新的问题和瓶颈。所以,目前也只有保守地说,“至少能带1百万人同时在线”。
但是,可以肯定的是,在保证响应速度、稳定性的前提下,UFO发布里自带的“喀秋莎-联网扫雷”是最大负载能力的设计。
在UFO Web Server的/conf/server.xml里<Connector>标签有一个web属性,把它写成这样:
web="off"
UFO Web Server就不支持普通的http协议(但支持创建WebSocket的http连接请求),这样您的UFO Web Server就不支持用户的网页访问,是一个纯粹的网游服务器了。
其实采用WebSocket开发与采用普通的Socket开发,在接近网络底层方面是完全一样的。
当有新Socket连接来了时,WSProcessor的onOpen(WSClient client)方法被立即调用;
当有新的数据来了时,WSProcessor的onData(WSClient client, byte[] bs)方法立即调用;
如果您想关掉Socket,可以执行WSEndpoint的close(WSClient aclient)方法;
如果您想知道,Socket对端的IP,可以执行WSEndpoint的getRemoteSocketAddress(WSClient aclient)方法;
......
等等。
UFO发布里自带的“喀秋莎-联网扫雷”网游不够标准吗?设计不够先进吗?运行起来有问题吗?
另外,www.gm365.com今后的联网游戏以及其它要求高负载的联网应用都采用UFO的WebSocket开发。对于以前有的联网游戏,除了两款已经发布了足够多的手机客户端以外,全部都采用UFO的WebSocket重写。
TT的后台只有2种进程:UFO和数据库进程,TT就是采用UFO的CS模式的WebSocket开发的。大体上,TT=微信+QQ+联众+陌陌,是一个应有尽有的联网应用。
在examples这个Context里有个websocket子目录,UFO的websocket例子都在里面。其中BS子目录有3个例子,借用了Tomcat的例子,演示了BS模式的WebSocket;其中的CS子目录,有4个例子,演示了CS模式的WebSocket。
CS模式的hello和many例子,以最简单的方式演示了如何创建UFO的CS模式的WebSocket;TTChat这个例子初步演示了如何利用UFO的CS模式的WebSocket进行联网应用开发;mine这个例子,即“喀秋莎-联网扫雷”,是一个完整的最先进的负载能力最大化设计的利用UFO的CS模式的WebSocket进行联网应用开发的例子,也是我公司未来联网游戏的构架。
你先要搞明白大的业务逻辑和大的程序逻辑。
1. 这个联网游戏假定了一台服务器能带1百万人甚至更多的同时在线,(而每个玩区只有300人),这是个最大的
逻辑。
2. 这个联网游戏在设计上保证用户在任何时候都能登录服务器玩游戏。
即使服务器程序写得再稳定,但是由于服务器硬件坏了或机房出事等不可抗力的原因,假定服务器不宕是错误的。
这个游戏采用2个(或更多)游戏总站Servlet和2个(或更多)游戏Servlet的方案,保证用户在任何时候得到服务。游戏总站Servlet的IP(采用域名), port和uri固定地放在client端,用户登录发现一个总站连不通,就连到另外一个总站,只有那些总站同时都宕了,用户才无法登陆服务器。
但是,细心的技术人员会发现:这个联网游戏还是没做到在任何时候,用户都能登陆,因数据库只有一个服务器,当这个数据库服务器宕了时,用户无法登陆玩游戏了。
这的确是个问题。目前,gm365的现役服务器体系也没做到双数据库服务器随时使用(这可能是个技术难点,容易造成数据不一致)。目前,gm365的现役服务器体系是采用双数据库运行,其中一个做热倍份。
这个游戏的游戏总站Servlet在examples这个Context下的\WEB-INF\classes\websocket\CS\mine\zz里,总站Servlet可以运行在1个或多个服务器上,它负责用户的登陆和注册。
这个游戏的游戏Servlet在examples这个Context下的\WEB-INF\classes\websocket\CS\mine里,这个Servlet也可以运行在1个或多个服务器上,它是用户玩游戏的服务器程序。每个Servlet实例里有m个站点*n个玩区,每个玩区能容纳300人。每个Servlet的站点在总站里的编号是靠onum这个成员变量来确定的。
在第二次世界大战的关键的苏德战争的相持时期,苏联发明了一种“喀秋莎”火箭炮,它一次能发射2x8枚炮弹,火力密集;它载于卡车或坦克上,机动灵活。“喀秋莎”火箭炮为苏联打赢苏德战争发挥了重要作用。
本联网扫雷游戏“车载”于UFO Web Server,一个Servlet带m*n玩区,只要服务器内存够用,可以布置任意多,因此把这个游戏取名为“喀秋莎-联网扫雷”。
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: localhost\r\n\r\n").getBytes());
请问,这里的Host是什么意思?
如果您在conf/server.xml里配置了多个"Host"标签,也即多个虚拟主机,在CS模式的WebSocket联网时,需要填写"Host"的http头,否则,UFO就可能找不到正确的servlet。
这个"Host"的http头的值是conf/server.xml里配置了多个"Host"标签的"name"的值或Alias. 例如,假设您的conf/server.xml的配置是这样的:
<Host appBase="webapps" name="www.gm365.com">
<Alias>gm365.com</Alias>
<Alias>222.73.205.120</Alias>
<Alias>localhost</Alias>
<Alias>127.0.0.1</Alias>
<!--
<welcome-file>index.html</welcome-file>
-->
<Context path="/examples" docBase="webapps/examples" >
</Context>
<Context path="/struts2_demo" docBase="webapps/struts2_demo" >
</Context>
<Context path="" docBase="webapps/ROOT" >
</Context>
</Host>
那么,HelloCS.java在联网时,写成:
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: www.gm365.com\r\n\r\n").getBytes());
或:
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: gm365.com\r\n\r\n").getBytes());
或:
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: 222.73.205.120\r\n\r\n").getBytes());
或:
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: localhost\r\n\r\n").getBytes());
或:
ou.write(("GET /examples/websocket/CS/hello HTTP/1.1\r\n").getBytes());
ou.write(("Host: 127.0.0.1\r\n\r\n").getBytes());
都是正确的。