分类: 云计算

云计算

nginx反向代理模块

nginx的反向代理模块有很多种配置,下面介绍一些常用的配置实例:

1.proxy_pass

语法:   proxy_pass URL
配置块:   location,if
详解:此配置项将当前请求反向代理到URL参数指定的服务器上,URL可以是主机名或者IP地址加端口的形式。例如:

  1. // nginx.conf配置文件
  2. // 配置URL地址
  3. proxy_pass http://www.54rd.net/html/webserver/;
  4. // 也可以配置unix句柄
  5. proxy_pass http://unix:/path/to/backend.sock:/webserver/;
  6. // 也可以把HTTP转换成更安全的HTTPS
  7. proxy_pass https://192.168.0.1;

默认情况下反向代理是不会转发请求中的Host头部。如果需要转发,那么必须加上set_header配置:

  1. proxy_set_header Host $host;

2.proxy_method

语法:proxy_method method;
配置块:http,server,location
详解:此配置项表示转发时的协议方法名,例如:

  1. // 配置后客户端发来的GET请求在转发时方法名也会改为POST
  2. proxy_method POST;

3.proxy_hide_header

语法:proxy_hide_header the_header;
配置块:http,server,location
详解:nginx会将上游服务器的响应转发给客户端,但默认不会转发以下HTTP头部字段:Date,Server,X-Pad和X-Accel-*。使用proxy_hide_header后可以任意指定哪些HTTP头部字段不能被转发。例如:

  1. // 例如不转发缓存控制
  2. proxy_hide_header Cache-Control;

4.proxy_pass_header

语法:proxy_pass_header the_header;
配置块:http,server,location
详解:于proxy_hide_header功能相反,proxy_pass_header会将原来禁止转发的header设置成允许转发。例如:

  1. // 允许重定向
  2. proxy_pass_header X-Accel-Redirect;

5.proxy_pass_request_body

语法:proxy_pass_request_body on|off;
默认:proxy_pass_request_body on;
配置块:http,server,location
详解:作用为确定是否向上游服务器发送HTTP包体部分。

6.proxy_pass_request_headers

语法:proxy_pass_request_headers on|off;
默认:proxy_pass_request_headers on;
配置块:http,server,location
详解:作用为确定是否转发HTTP头部。

7.proxy_redirect

语法:proxy_redirect [default|off|redirect replacement];
默认:proxy_redirect default;
配置块:http,server,location
详解:当上游服务器返回的响应时重定向或者刷新请求(如HTTP响应码是302或者301)时,proxy_redirect可以重设HTTP头部的location或refresh字段,例如:

  1. // 如果上游服务器发出的响应码是302,location字段的URL是http://www.54rd.net/html/webserver/,那么实际会转发到http://www.54rd.net/html/php/
  2. proxy_redirect http://www.54rd.net/html/webserver/ http://www.54rd.net/html/php/;
  3. // 还可以使用ngx-http-core-module提供的变量来设置
  4. proxy_redirect http://www.54rd.net/html/webserver/ http://$host:$server_port/;
  5. // 也可以省略repalcement参数中的主机部分,这时会用虚拟主机名称来填充
  6. proxy_redirect http://www.54rd.net/html/webserver/ /html/php/;

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

在使用nginx的反向代理功能时,有时会出现重定向的url不是我想要的url,例如下面的例子:前端的Nginx负责把http: www 54rd net yum Server 开头的url反向代理到后端的http: 192 168 1 1 Server 上。对于有完整
在使用nginx的反向代理功能时,有时会出现重定向的url不是我想要的url,例如下面的例子:

前端的Nginx负责把http://www.54rd.net/yum/Server/开头的url反向代理到后端的http://192.168.1.1/Server/上。

对于有完整的路径,如http://www.54rd.net/yum/Server/的代理没有问题,Server对应后台服务器的一个目录。

但当访问  http://www.54rd.net/yum/Server时,后端Nginx会发送一个301到/上,于是返回到前端后URL变成了 http://www.54rd.net/Server/,这个url显然不是我们想要的。

在Apache中有个ProxyPassReverse的参数,用来调整反向代理服务器发送的http应答头的url,可以解决这个问题。

查了Nginx的手册后,终于发现了proxy_redirect这个参数,它实现的功能和ProxyPassReverse类似,例如增加如下配置:

  1. location ^~ /yum
  2. {
  3.             proxy_pass http://192.168.1.1/;
  4.             proxy_redirect http://www.54rd.net/ /yum/;
  5. }

这样,当访问http://www.54rd.net/yum/Server后,就会301到http://www.54rd.net/yum/Server/上了。

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

8.proxy_next_upstream

语法:proxy_next_upstream [error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off];
默认:proxy_next_upstream error timeout;
配置块:http,server,location
详解:此配置项表示当向一台上游服务器转发请求出现错误时,继续换一台上游服务器处理这个请求,这样可以更好的保证客户端只收到来自一个上游服务器的应答。proxy_next_upstream的参数用来说明在哪些情况下会继续选择下一台上游服务器转发请求:

  1. error:当向上游服务器发起连接,发送请求,读取响应时出错时。
  2. timeout:发送请求或者读取响应发生超时时。
  3. invalid_header:上游服务器发送的响应时不合法时。
  4. http_500:上游服务器返回的HTTP响应码是500时。
  5. http_502:上游服务器返回的HTTP响应码是502时。
  6. http_503:上游服务器返回的HTTP响应码是503时。
  7. http_504:上游服务器返回的HTTP响应码是504时。
  8. http_404:上游服务器返回的HTTP响应码是404时。
  9. off:关闭proxy_next_upstream 功能一出错就选择另一台上游服务器再次转发。

小结:nginx的反向代理使用起来还是很方便的,适当的修改配置即可。

PV、UV、IP之间的区别与联系

PV是网站分析的一个术语,用以衡量网站用户访问的网页的数量。对于广告主,PV值可预期它可以带来多少广告收入。一般来说,PV与来访者的数量成正比,但是PV并不直接决定页面的真实来访者数量,如同一个来访者通过不断的刷新页面,也可以制造出非常高的PV。
   PV、UV、IP之间的区别与联系 - 以德糊人 - ——挨踢民工 Playkid——
   1、什么是PV值
  PV(page view)即页面浏览量或点击量,是衡量一个网站或网页用户访问量。具体的说,PV值就是所有访问者在24小时(0点到24点)内看了某个网站多少个页面或某个网页多少次。PV是指页面刷新的次数,每一次页面刷新,就算做一次PV流量。
  度量方法就是从浏览器发出一个对网络服务器的请求(Request),网络服务器接到这个请求后,会将该请求对应的一个网页(Page)发送给浏览器,从而产生了一个PV。那么在这里只要是这个请求发送给了浏览器,无论这个页面是否完全打开(下载完成),那么都是应当计为1个PV。
   2、什么是UV值
  UV(unique visitor)即独立访客数,指访问某个站点或点击某个网页的不同IP地址的人数。在同一天内,UV只记录*次进入网站的具有独立IP的访问者,在同一天内再次访问该网站则不计数。UV提供了一定时间内不同观众数量的统计指标,而没有反应出网站的全面活动。通过IP和cookie是判断UV值的两种方式:
   用Cookie分析UV值
  当客户端*次访问某个网站服务器的时候,网站服务器会给这个客户端的电脑发出一个Cookie,通常放在这个客户端电脑的C盘当中。在这个Cookie中会分配一个独一无二的编号,这其中会记录一些访问服务器的信息,如访问时间,访问了哪些页面等等。当你下次再访问这个服务器的时候,服务器就可以直接从你的电脑中找到上一次放进去的Cookie文件,并且对其进行一些更新,但那个独一无二的编号是不会变的。
   PV、UV、IP之间的区别与联系 - 以德糊人 - ——挨踢民工 Playkid——
   3、IP即独立IP数
  IP可以理解为独立IP的访问用户,指1天内使用不同IP地址的用户访问网站的数量,同一IP无论访问了几个页面,独立IP数均为1。但是假如说两台机器访问而使用的是同一个IP,那么只能算是一个IP的访问。
  IP和UV之间的数据不会有太大的差异,通常UV量和比IP量高出一点,每个UV相对于每个IP更准确地对应一个实际的浏览者。
  ①UV大于IP
  这种情况就是在网吧、学校、公司等,公用相同IP的场所中不同的用户,或者多种不同浏览器访问您网站,那么UV数会大于IP数。
  ②UV小于IP
  在家庭中大多数电脑使用ADSL拨号上网,所以同一个用户在家里不同时间访问您网站时,IP可能会不同,因为它会根据时间变动IP,即动态的IP地址,但是实际访客数唯一,便会出现UV数小于IP数。

十个免费的 Web 压力测试工具

本文列举了是十个免费工具,可以用来进行Web的负载/压力测试的。这样你就可以知道你的服务器以及你的WEB应用能够扛得住多少的并发量,以及网站性能。

0. Grinder –  Grinder是一个开源的JVM负载测试框架,它通过很多负载注射器来为分布式测试提供了便利。 支持用于执行测试脚本的Jython脚本引擎HTTP测试可通过HTTP代理进行管理。根据项目网站的说法,Grinder的 主要目标用户是“理解他们所测代码的人——Grinder不仅仅是带有一组相关响应时间的‘黑盒’测试。由于测试过程可以进行编码——而不是简单地脚本 化,所以程序员能测试应用中内部的各个层次,而不仅仅是通过用户界面测试响应时间。

The Grinder

1. Pylot -Pylot 是一款开源的测试web service性能和扩展性的工具,它运行HTTP 负载测试,这对容量计划,确定基准点,分析以及系统调优都很有用处。Pylot产生并发负载(HTTP Requests),检验服务器响应,以及产生带有metrics的报表。通过GUI或者shell/console来执行和监视test suites。

Pylot Main Logo

 

2. Web Capacity Analysis Tool (WCAT) – 这是一种轻量级负载生成实用工具,不仅能够重现对 Web 服务器(或负载平衡服务器场)的脚本 HTTP 请求,同时还可以收集性能统计数据供日后分析之用。WCAT 是多线程应用程序,并且支持从单个源控制多个负载测试客户端,因此您可以模拟数千个并发用户。该实用工具利用您的旧机器作为测试客户端,其中每个测试客户 端又可以产生多个虚拟客户端(*大数量取决于客户端机器的网络适配器和其他硬件)。您可以选择使用 HTTP 1.0 还是 HTTP 1.1 请求,以及是否使用 SSL。并且,如果测试方案需要,您还可以使用脚本执行的基本或 NTLM 身份验证来访问站点的受限部分。(如果您的站点使用 cookie、表单或基于会话的身份验证,那您可以创建正确的 GET 或 POST 请求来对测试用户进行身份验证。)WCAT 还可管理您站点可能设置的任何 cookie,所以配置文件和会话信息将永久保存。

3. fwptt – fwptt 也是一个用来进行WEB应用负载测试的工具。它可以记录一般的请求,也可以记录Ajax请求。它可以用来测试 asp.net, jsp, php 或是其它的Web应用。

4. JCrawler – JCrawler是一个开源(CPL) 的WEB应用压力测试工具。通过其名字,你就可以知道这是一个用Java写的像网页爬虫一样的工具。只要你给其几个URL,它就可以开始爬过去了,它用一 种特殊的方式来产生你WEB应用的负载。这个工具可以用来测试搜索引擎对你站点产生的负载。当然,其还有另一功能,你可以建立你的网站地图和再点击一下, 将自动提交Sitemap给前5名的搜索引擎!

5. Apache JMeter – Apache JMeter是一个专门为运行和服务器装载测试而设计的、100%的纯Java桌面运行程序。原先它是为Web/HTTP测试而设计的,但是它已经扩展以 支持各种各样的测试模块。它和用于HTTP和SQL数据库(使用JDBC)的模块一起运送。它可以用来测试静止资料库或者活动资料库中的服务器的运行情 况,可以用来模拟对服务器或者网络系统加以重负荷以测试它的抵抗力,或者用来分析不同负荷类型下的所有运行情况。它也提供了一个可替换的界面用来定制数据 显示,测试同步及测试的创建和执行。

6. Siege -Siege(英文意思是围攻)是一个压力测试和评测工具,设计用于WEB开发这评估应用在压力下的承受能力:可以根据配置对一个WEB站点进行多用户的并发访问,记录每 个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。 Siege 支持基本的认证,cookies, HTTP 和 HTTPS 协议。

7. http_load – http_load 以并行复用的方式运行,用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死。可以可以测试HTTPS类的网站请求。

8. Web Polygraph – Web Polygraph这个软件也是一个用于测试WEB性能的工具,这个工具是很多公司的标准测试工具,包括微软在分析其软件性能的时候,也是使用这个工具做为基准工具的。很多招聘测试员的广告中都注明需要熟练掌握这个测试工具。

9. OpenSTA – OpenSTA是一个免费的、开放源代码的web性能测试工具,能录制功能非常强大的脚本过程,执行性能测试。例如虚拟多个不同的用户同时登陆被测试网 站。其还能对录制的测试脚本进行,按指定的语法进行编辑。在录制完测试脚本后,可以对测试脚本进行编辑,以便进行特定的性能指标分析。其较为丰富的图形化 测试结果大大提高了测试报告的可阅读性。OpenSTA 基于CORBA 的结构体系,它通过虚拟一个proxy,使用其专用的脚本控制语言,记录通过 proxy 的一切HTTP/S traffic。通过分析OpenSTA的性能指标收集器收集的各项性能指标,以及HTTP 数据,对系统的性能进行分析。

AMD异步模块定义介绍和Require.js中使用jQuery及jQuery插件的方法

AMD 模块

AMD(异步模块定义,Asynchronous Module Definition)格式总体的目标是为现在的开发者提供一个可用的模块化 JavaScript 的解决方案。

AMD 模块格式本身是一个关于如何定义模块的提案,在这种定义下模块和依赖项都能够异步地进行加载。它有很多独特的优势,包括天生的异步及高度灵活等特性,这些特性能够解除常见的代码与模块标识间的那种紧密耦合。目前它已经被很多项目所接纳,包括jQuery(1.7)。

RequireJS

RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范。

jQuery 对AMD的支持

jQuery 1.7 开始支持将 jQuery 注册为一个AMD异步模块。有很多兼容的脚本加载器(包括 RequireJS 和 curl)都可以用一个异步模块格式来加载模块,这也就表示不需要太多 hack 就能让一切运行起来。可以看看jQuery 1.7 中的源码:

代码如下:
// Expose jQuery as an AMD module , but only for AMD loaders that
// understand the issues with loading multiple versions of jQuery
// in a page that all might call define().
  —-仍然可以采用常规用法,amd模式只有在amd加载器调用时才走amd模式。
//The loader will indicate   they have special allowances for multiple jQuery versions by
// specifying define.amd.jQuery = true. Register as a named module,
// since jQuery can be concatenated with other files that may use define,
// but not use a proper concatenation script that understands anonymous
// AMD modules. A named AMD is safest and most robust way to register.
// Lowercase jquery is used because AMD module names are derived from
// file names, and jQuery is normally delivered in a lowercase file name .
if ( typeof define === “function” && define.amd && define.amd.jQuery ) {
define( “jquery”, [], function () { return jQuery; } );
}

 

其工作的原理是,所使用的脚本加载器通过指定一个属性,即 define.amd.jQuery 为 true,来标明自己可以支持多个 jQuery 版本。如果有兴趣了解特定的实现细节的话,我们可以将 jQuery 注册为一个具名模块,因为可能会有这样的风险,即它可能被与其它使用了 AMD 的 define() 方法的文件拼合在一起,而没有使用一个合适的、理解匿名 AMD 模块定义的拼合脚本。

高版本的jQuery (1.11.1) 去掉了define.amd.jQuery判断:

代码如下:

if ( typeof define === “function” && define.amd ) {
define( “jquery”, [], function() {
return jQuery;
});
}

 

Require.js中使用jQuery

Require.js中使用jQuery非常方便,简单配置就可以了,例如:

代码如下:

// 简单的配置
require.config({

// RequireJS 通过一个相对的路径 baseUrl来加载所有代码。baseUrl通常被设置成data-main属性指定脚本的同级目录。
baseUrl: “./js”,

// 第三方脚本模块的别名,jquery比libs/jquery-1.11.1.min.js简洁明了;
paths: {

“jquery”: “libs/jquery-1.11.1.min.js”

}

});

// 开始使用jQuery 模块
require([“jquery”], function ($) {

//你的代码
//这里直接可以使用jquery的方法,比如:$( “#result” ).html( “Hello World!” );

});

 

Require.js中使用jQuery 插件

虽然jQuery的支持AMD的API, 这并不意味着jQuery插件也是和AMD兼容的。

一般的jQuery 插件格式:

代码如下:
(function ($) {
$.fn.m??yPlugin = function () {
//你自己的插件代码
};
})(jQuery);

不过我们稍微修改一下就可以使用Require.js加载一个jQuery插件:

代码如下:
;(function (factory) {
if (typeof define === “function” && define.amd) {
// AMD模式
define([ “jquery” ], factory);
} else {
// 全局模式
factory(jQuery);
}
}(function ($) {
$.fn.jqueryPlugin = function () {
//插件代码
};
}));

或者这样:

define(["jquery"], // Require jquery

       function($){
//把你原来的插件代码放这里吧,这样就行了
//注意文件命名哦
});

Require.js中使用jQuery UI组件

Require.js中使用jQuery UI组件也类似的,只要改造一下jQuery Widget Factory 代码就可以了,并且感觉jQuery UI的依赖关系加载就可以了。例如:

代码如下:
(function (widgetFactory) {

if (typeof define === “function” && define.amd) {
// AMD模式
define(“jquery.ui.widget”, [“jquery”], function () {

widgetFactory(window.jQuery);

});
} else {
// 全局模式
widgetFactory(window.jQuery);
}
}
(function ($, undefined) {
// jQuery Widget Factory 代码
}));

AMD, CommonJS, UMD简介

*近几年,我们可以选择的Javascript组件的生态系统一直在稳步增长。虽然陡增的选择范围是*好的,但当组件混合匹配使用时就会出现很尴尬的局面。开发新手们会很快发现不是所有组件都能彼此“和平相处”。

为了解决这个问题,两种竞争关系的模块规范AMD和CommonJS问世了,它们允许开发者遵照一种约定的沙箱化和模块化的方式来写代码,这样就能避免“污染生态系统”。

AMD

随着RequireJS成为*流行的实现方式,异步模块规范(AMD)在前端界已经被广泛认同。

下面是只依赖jquery的模块foo的代码:

  1. // 文件名: foo.js
  2. define([“jquery”], function ($) {
  3. // 方法
  4. function myFunc(){};
  5. // 暴露公共方法
  6. return myFunc;
  7. });

还有稍微复杂点的例子,下面的代码依赖了多个组件并且暴露多个方法:

  1. // 文件名: foo.js
  2. define([“jquery”, “underscore”], function ($, _) {
  3. // 方法
  4. function a(){}; // 私有方法,因为没有被返回(见下面)
  5. function b(){}; // 公共方法,因为被返回了
  6. function c(){}; // 公共方法,因为被返回了
  7. // 暴露公共方法
  8. return {
  9. b: b,
  10. c: c
  11. }
  12. });

定义的*个部分是一个依赖数组,第二个部分是回调函数,只有当依赖的组件可用时(像RequireJS这样的脚本加载器会负责这一部分,包括找到文件路径)回调函数才被执行。

注意,依赖组件和变量的顺序是一一对应的(例如,jquery->$, underscore->_)。

同时注意,我们可以用任意的变量名来表示依赖组件。假如我们把$改成

jQuery$,在函数体里面的所有对jQuery的引用都由$变成了

。还要注意,*重要的是你不能在回调函数外面引用变量$和_,因为它相对其它代码是独立的。这正是模块化的目的所在!

CommonJS

如果你用Node写过东西的话,你可能会熟悉CommonJS的风格(node使用的格式与之相差无几)。因为有Browserify,它也一直被前端界广泛认同。

就像前面的格式一样,下面是用CommonJS规范实现的foo模块的写法:

  1. // 文件名: foo.js
  2. // 依赖
  3. var $ = require(“jquery”);
  4. // 方法
  5. function myFunc(){};
  6. // 暴露公共方法(一个)
  7. module.exports = myFunc;

还有更复杂的例子,下面的代码依赖了多个组件并且暴露多个方法:

  1. // 文件名: foo.js
  2. var $ = require(“jquery”);
  3. var _ = require(“underscore”);
  4. // methods
  5. function a(){}; // 私有方法,因为它没在module.exports中 (见下面)
  6. function b(){}; // 公共方法,因为它在module.exports中定义了
  7. function c(){}; // 公共方法,因为它在module.exports中定义了
  8. // 暴露公共方法
  9. module.exports = {
  10. b: b,
  11. c: c
  12. };

AMD与CMD的区别

CMD相当于按需加载,定义一个模块的时候不需要立即制定依赖模块,在需要的时候require就可以了,比较方便;而AMD则相反,定义模块的时候需要制定依赖模块,并以形参的方式引入factory中。

UMD: 通用模块规范

既然CommonJs和AMD风格一样流行,似乎缺少一个统一的规范。所以人们产生了这样的需求,希望有支持两种风格的“通用”模式,于是通用模块规范(UMD)诞生了。

不得不承认,这个模式略难看,但是它兼容了AMD和CommonJS,同时还支持老式的“全局”变量规范:

  1. (function (root, factory) {
  2. if (typeof define === “function” && define.amd) {
  3. // AMD
  4. define([“jquery”], factory);
  5. } else if (typeof exports === “object”) {
  6. // Node, CommonJS之类的
  7. module.exports = factory(require(“jquery”));
  8. } else {
  9. // 浏览器全局变量(root 即 window)
  10. root.returnExports = factory(root.jQuery);
  11. }
  12. }(this, function ($) {
  13. // 方法
  14. function myFunc(){};
  15. // 暴露公共方法
  16. return myFunc;
  17. }));
注:echarts中的中国地图china.js就采用的这种方式

保持跟上面例子一样的模式,下面是更复杂的例子,它依赖了多个组件并且暴露多个方法:

  1. (function (root, factory) {
  2. if (typeof define === “function” && define.amd) {
  3. // AMD
  4. define([“jquery”, “underscore”], factory);
  5. } else if (typeof exports === “object”) {
  6. // Node, CommonJS之类的
  7. module.exports = factory(require(“jquery”), require(“underscore”));
  8. } else {
  9. // 浏览器全局变量(root 即 window)
  10. root.returnExports = factory(root.jQuery, root._);
  11. }
  12. }(this, function ($, _) {
  13. // 方法
  14. function a(){}; // 私有方法,因为它没被返回 (见下面)
  15. function b(){}; // 公共方法,因为被返回了
  16. function c(){}; // 公共方法,因为被返回了
  17. // 暴露公共方法
  18. return {
  19. b: b,
  20. c: c
  21. }
  22. }));

RequireJS和AMD规范

概述

RequireJS是一个工具库,主要用于客户端的模块管理。它可以让客户端的代码分成一个个模块,实现异步或动态加载,从而提高代码的性能和可维护性。它的模块管理遵守AMD规范(Asynchronous Module Definition)。

RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。

首先,将require.js嵌入网页,然后就能在网页中进行模块化编程了。

 <script data-main="scripts/main" src="scripts/require.js"></script>

上面代码的data-main属性不可省略,用于指定主代码所在的脚本文件,在上例中为scripts子目录下的main.js文件。用户自定义的代码就放在这个main.js文件中。

define方法:定义模块

define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。

按照是否依赖其他模块,可以分成两种情况讨论。*种情况是定义独立模块,即所定义的模块不依赖其他模块;第二种情况是定义非独立模块,即所定义的模块依赖于其他模块。

(1)独立模块

如果被定义的模块是一个独立模块,不需要依赖任何其他模块,可以直接用define方法生成。

  1. define({
  2. method1: function() {},
  3. method2: function() {},
  4. });

上面代码生成了一个拥有method1、method2两个方法的模块。

另一种等价的写法是,把对象写成一个函数,该函数的返回值就是输出的模块。

  1. define(function () {
  2. return {
  3. method1: function() {},
  4. method2: function() {},
  5. };
  6. });

后一种写法的自由度更高一点,可以在函数体内写一些模块初始化代码。

值得指出的是,define定义的模块可以返回任何值,不限于对象。

(2)非独立模块

如果被定义的模块需要依赖其他模块,则define方法必须采用下面的格式。

  1. define([‘module1’, ‘module2’], function(m1, m2) {
  2. });

define方法的*个参数是一个数组,它的成员是当前模块所依赖的模块。比如,[‘module1’, ‘module2’]表示我们定义的这个新模块依赖于module1模块和module2模块,只有先加载这两个模块,新模块才能正常运行。一般情况下,module1模块和module2模块指的是,当前目录下的module1.js文件和module2.js文件,等同于写成[’./module1’, ‘./module2’]。

define方法的第二个参数是一个函数,当前面数组的所有成员加载成功后,它将被调用。它的参数与数组的成员一一对应,比如function(m1, m2)就表示,这个函数的*个参数m1对应module1模块,第二个参数m2对应module2模块。这个函数必须返回一个对象,供其他模块调用。

  1. define([‘module1’, ‘module2’], function(m1, m2) {
  2. return {
  3. method: function() {
  4. m1.methodA();
  5. m2.methodB();
  6. }
  7. };
  8. });

上面代码表示新模块返回一个对象,该对象的method方法就是外部调用的接口,menthod方法内部调用了m1模块的methodA方法和m2模块的methodB方法。

需要注意的是,回调函数必须返回一个对象,这个对象就是你定义的模块。

如果依赖的模块很多,参数与模块一一对应的写法非常麻烦。

  1. define(
  2. [ ‘dep1’, ‘dep2’, ‘dep3’, ‘dep4’, ‘dep5’, ‘dep6’, ‘dep7’, ‘dep8’],
  3. function(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8){
  4. }
  5. );

为了避免像上面代码那样繁琐的写法,RequireJS提供一种更简单的写法。

  1. define(
  2. function (require) {
  3. var dep1 = require(‘dep1’),
  4. dep2 = require(‘dep2’),
  5. dep3 = require(‘dep3’),
  6. dep4 = require(‘dep4’),
  7. dep5 = require(‘dep5’),
  8. dep6 = require(‘dep6’),
  9. dep7 = require(‘dep7’),
  10. dep8 = require(‘dep8’);
  11. }
  12. });

下面是一个define实际运用的例子。

  1. define([‘math’, ‘graph’],
  2. function ( math, graph ) {
  3. return {
  4. plot: function(x, y){
  5. return graph.drawPie(math.randomGrid(x,y));
  6. }
  7. }
  8. };
  9. );

上面代码定义的模块依赖math和graph两个库,然后返回一个具有plot接口的对象。

另一个实际的例子是,通过判断浏览器是否为IE,而选择加载zepto或jQuery。

  1. define((‘__proto__’ in {} ? [‘zepto’] : [‘jquery’]), function($) {
  2. return $;
  3. });

上面代码定义了一个中间模块,该模块先判断浏览器是否支持__proto__属性(除了IE,其他浏览器都支持),如果返回true,就加载zepto库,否则加载jQuery库。

require方法:调用模块

require方法用于调用模块。它的参数与define方法类似。

  1. require([‘foo’, ‘bar’], function ( foo, bar ) {
  2. foo.doSomething();
  3. });

上面方法表示加载foo和bar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。

require方法的*个参数,是一个表示依赖关系的数组。这个数组可以写得很灵活,请看下面的例子。

  1. require( [ window.JSON ? undefined : ‘util/json2’ ], function ( JSON ) {
  2. JSON = JSON || window.JSON;
  3. console.log( JSON.parse( ‘{ “JSON” : “HERE” }’ ) );
  4. });

上面代码加载JSON模块时,首先判断浏览器是否原生支持JSON对象。如果是的,则将undefined传入回调函数,否则加载util目录下的json2模块。

require方法也可以用在define方法内部。

  1. define(function (require) {
  2. var otherModule = require(‘otherModule’);
  3. });

下面的例子显示了如何动态加载模块。

  1. define(function ( require ) {
  2. var isReady = false, foobar;
  3. require([‘foo’, ‘bar’], function (foo, bar) {
  4. isReady = true;
  5. foobar = foo() + bar();
  6. });
  7. return {
  8. isReady: isReady,
  9. foobar: foobar
  10. };
  11. });

上面代码所定义的模块,内部加载了foo和bar两个模块,在没有加载完成前,isReady属性值为false,加载完成后就变成了true。因此,可以根据isReady属性的值,决定下一步的动作。

下面的例子是模块的输出结果是一个promise对象。

  1. define([‘lib/Deferred’], function( Deferred ){
  2. var defer = new Deferred();
  3. require([‘lib/templates/?index.html’,‘lib/data/?stats’],
  4. function( template, data ){
  5. defer.resolve({ template: template, data:data });
  6. }
  7. );
  8. return defer.promise();
  9. });

上面代码的define方法返回一个promise对象,可以在该对象的then方法,指定下一步的动作。

如果服务器端采用JSONP模式,则可以直接在require中调用,方法是指定JSONP的callback参数为define。

  1. require( [
  2. “http://someapi.com/foo?callback=define”
  3. ], function (data) {
  4. console.log(data);
  5. });

require方法允许添加第三个参数,即错误处理的回调函数。

  1. require(
  2. [ “backbone” ],
  3. function ( Backbone ) {
  4. return Backbone.View.extend({ /* … */ });
  5. },
  6. function (err) {
  7. // …
  8. }
  9. );

require方法的第三个参数,即处理错误的回调函数,接受一个error对象作为参数。

require对象还允许指定一个全局性的Error事件的监听函数。所有没有被上面的方法捕获的错误,都会被触发这个监听函数。

  1. requirejs.onError = function (err) {
  2. // …
  3. };

AMD模式小结

define和require这两个定义模块、调用模块的方法,合称为AMD模式。它的模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。

AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。

配置require.js:config方法

require方法本身也是一个对象,它带有一个config方法,用来配置require.js运行参数。config方法接受一个对象作为参数。

  1. require.config({
  2. paths: {
  3. jquery: [
  4. ‘//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js’,
  5. ‘lib/jquery’
  6. ]
  7. }
  8. });

config方法的参数对象有以下主要成员:

(1)paths

paths参数指定各个模块的位置。这个位置可以是同一个服务器上的相对位置,也可以是外部网址。可以为每个模块定义多个位置,如果*个位置加载失败,则加载第二个位置,上面的示例就表示如果CDN加载失败,则加载服务器上的备用脚本。需要注意的是,指定本地文件路径时,可以省略文件*后的js后缀名。

  1. require([“jquery”], function($) {
  2. // …
  3. });

上面代码加载jquery模块,因为jquery的路径已经在paths参数中定义了,所以就会到事先设定的位置下载。

(2)baseUrl

baseUrl参数指定本地模块位置的基准目录,即本地模块的路径是相对于哪个目录的。该属性通常由require.js加载时的data-main属性指定。

(3)shim

有些库不是AMD兼容的,这时就需要指定shim属性的值。shim可以理解成“垫片”,用来帮助require.js加载非AMD规范的库。

  1. require.config({
  2. paths: {
  3. “backbone”: “vendor/backbone”,
  4. “underscore”: “vendor/underscore”
  5. },
  6. shim: {
  7. “backbone”: {
  8. deps: [ “underscore” ],
  9. exports: “Backbone”
  10. },
  11. “underscore”: {
  12. exports: “_”
  13. }
  14. }
  15. });

上面代码中的backbone和underscore就是非AMD规范的库。shim指定它们的依赖关系(backbone依赖于underscore),以及输出符号(backbone为“Backbone”,underscore为“_”)。

插件

RequireJS允许使用插件,加载各种格式的数据。完整的插件清单可以查看官方网站

下面是插入文本数据所使用的text插件的例子。

  1. define([
  2. ‘backbone’,
  3. ‘text!templates.html’
  4. ], function( Backbone, template ){
  5. // …
  6. });

上面代码加载的*个模块是backbone,第二个模块则是一个文本,用’text!’表示。该文本作为字符串,存放在回调函数的template变量中。

JDBC连接池、监控组件 Druid简介

Druid是一个JDBC组件,它包括三部分:

  • DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系。
  • DruidDataSource 高效可管理的数据库连接池。
  • SQLParser

Druid可以做什么?

1) 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。

2) 替换DBCPC3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。

3) 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。

4) SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-LoggingLog4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。

扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter-Chain机制,很方便编写JDBC层的扩展插件。

如下是一个基于Druid内置扩展StatFilter的监控实现:

%title插图%num

Druid 的 JavaDoc 文档请看

http://tool.oschina.net/apidocs/apidoc?api=druid0.26

浏览器端的AJAX缓存机制

AJAX的缓存是由浏览器维持的,对于发向服务器的某个url,ajax仅在*次请求时与服务器交互信息,之后的请求中,ajax不再向服务器提交请求,而是直接从缓存中提取数据。
有些情况下,我们需要每一次都从服务器得到更新后数据。思路是让每次请求的url都不同,而又不影响正常应用:在url之后加入随机内容。
e.g.

1
url=url+ "&" +Math.random();

Key points:
1.每次请求的url都不一样(ajax的缓存便不起作用)
2.不影响正常应用(*基本的)

这里我们由两条结论:

1:Ajax的缓存和HTTP的缓存是一样的
现代浏览器的HTTP和缓存机制比Ajax的XMLHttpRequest对象要差很多,所以它不认识也不关心Ajax请求.它仅仅是遵循普通的HTTP缓存规则,通过服务器返回的响应头来进行缓存.
如果你已经对 HTTP缓存 有了解,那么你可以把HTTP缓存的知识用对Ajax缓存的理解上. 他们只有一点不同的,就是设置响应头的方式会和普通文件不一样.
下面这些响应头可以让你的Ajax可缓存:
Expires: 这一项应该被设置成未来的某个合适的时间点,时间点的设置取决于内容变动的频繁程度.举个栗子,如果请求的是个库存数量,那么Expires的值可以是10秒以后.如果请求的是一个相片,那么Expires的值就可以久一点,因为它不会经常变动.Expires头可以让浏览器在一段时间内重用本地缓存数据,从而避免任何不必要的与服务器数据交互.
Last-Modified: 设置这一项是一个很好的选择,通过它,浏览器在发送条件性GET请求的时候会使用请求头里的 If-Modified-Since 来检查本地缓存的内容.如果数据不需要更新,服务器会返回304响应状态.
Cache-Control: 在合适的情况下,这个值应该被设置为 Public ,这样所有的中间代理和缓存都可以被保存并且与其他用户共享内容.在火狐里,它还支持HTTPS请求的缓存
当然,如果你使用POST方式发送Ajax是不能缓存的,因为POST请求永远不会被缓存.如果你的Ajax请求会产生其他作用(比如银行账户之间的转账),请使用POST请求.
我们设置了一个demo(这个demo已经不能看了ヽ(≧□≦)ノ)来阐明这些头信息是如何工作的. 在HttpWatch里,你可以看到我们在响应头信息里设置了以上三个响应头

2016621174746971.png (565×227)

如果你规律的点击 ‘Ajax Update’ 按钮,时间的改变会趋向于每隔一分钟一次.因为Expires响应头被设置为未来的一分钟.在下面这张截图里你可以看到:重复的点击更新按钮时,Ajax请求会读取浏览器本地的缓存而不会产生网络活动(发送和传输栏的值都是0)

2016621174823835.png (592×155)

*后一次1:06.531时刻的点击发送的Ajax请求产生了网络数据传输,因为缓存的数据已经超过了一分钟. 服务器返回200响应状态表示获取到了一份新的数据.
猜测这个demo应该是一个按钮,每点击一次获取一次当前时间然后回现在页面上.

2:IE浏览器在Expires时间过期之前不会刷新通过Ajax获取的内容.
有些时候,Ajax在页面加载的时候就被用来填充页面的某些部分(比如一个价格列表).它并不是通过用户的某个事件(比如点击某个按钮)触发的,而是在页面加载的时候就通过javascript来发送的.就好像Ajax请求和那些嵌入资源(比如js和css)是一样的.
如果你开发这样的页面,在刷新它的时候,很可能想要更新嵌入的Ajax请求内容.对于嵌入资源(CSS文件,图片等),浏览器会通过用户刷新的方式是F5(刷新)还是Ctrl+F5(强制刷新)来自动发送下列不同类型的请求:
1.F5(刷新): 如果请求内容带有 Last-Modified 响应头,那么浏览器会发送条件性更新请求. 它使用 If-Modified-Since 请求头进行比较,这样服务器就可以返回304状态来避免传输不必要的数据.
2.Ctrl+F5(强制刷新): 告诉浏览器发送无条件更新请求,请求头的 Cache-Control 被设置为‘no-cache’.这告诉所有的中间代理和缓存:浏览器需要获取*新的版本,无论它是否已经被缓存.
Firefox把这个刷新的方式传播到了那些在页面加载的时候就发送的Ajax请求上,把这些Ajax请求当成嵌入资源来处理.下面是HttpWatch在火狐下的截图,显示了Ajax Caching demo(这个demo已经不能看了ヽ(≧□≦)ノ)刷新(F5)页面时Ajax请求的效果:

2016621175010316.png (595×282)

火狐确保Ajax发起的请求是条件性的.在这个例子里,如果缓存数据不到10秒,服务器返回304,超过10秒,服务器返回200,重新传送数据.
在ie里,加载页面时就发起的Ajax请求被看做是和页面其他部分刷新毫无关系的,也不会被用户的刷新方式所左右.如果缓存的ajax数据没有过期,就不会有GET请求发送到服务器.它会直接从缓存里读取数据,从HttpWatch里看就是(Cache)结果.下面这个图是在ie下缓存没有过期的情况下按F5刷新:

2016621175032362.png (600×147)

就算是通过 Ctrl+F5 强制刷新,通过Ajax获取的数据也是从缓存里读取:

2016621175400871.png (583×130)

这就意味着,任何通过Ajax得到的内容如果没有过期,在ie下都不会被更新 – 即使你使用Ctrl+F5强制刷新. 唯一能确保你获取*新数据的方法就是手动清楚缓存. 可以使用HttpWatch的工具栏:

2016621175426365.png (587×258)

注意,Cache结果和304结果是不同的.Cache其实是200(cache),304就是304.Cache其实没有向服务器发送请求,可以从chrome里看到,它的耗时是0,response也是空.而304不同,
304请求是浏览器发起了一个条件性的请求,这个请求携带了 If-Modified-Since 请求头,如果这个文件在浏览器发送的这个时间之后没有修改过,服务器端就回返回一个304状态,告诉浏览器使用它本地的缓存内容.它没有Cache快,因为请求还是发送到了服务器端,只不过服务器端没有发送数据.
可以看下taobao首页,里面既有200(cache)也有304.可以查看他们的区别.

总结:

我们都知道,ajax能提高页面载入的速度的主要原因是通过ajax减少了重复数据的载入,真正做到按需获取,既然如此,我们在写ajax程序的时候不妨送佛送到西,在客户端再做一次缓存,进一步提高数据载入速度。那就是在载入数据的同时将数据缓存在浏览器内存中,一旦数据被载入,只要页面未刷新,该数据就永远的缓存在内存中,当用户再次查看该数据时,则不需要从服务器上去获取数据,*大的降低了服务器的负载和提高了用户的体验。

常用的消息摘要算法

天偶然的学习了一下几种关于消息摘要算法的知识。个人觉得很好。应着老话“好记性不如烂笔头”,我就码了几行代码咯。


算法嘛,没什么好说的了。毕竟是设计者智慧与汗水的结晶,也是时代进步的推动力。我们能做的就是将这种算法运用得当,造福全人类,就行了!

//格外需要注意的是采用CC(Commons codec)方式生成消息摘要时,一定要记得导入相关的jar包哦!
  • 1
  • 1

MD方式


  1. package MD;
  2. /**
  3. * MD:MessageDigest—-消息摘要算法
  4. */
  5. import java.security.MessageDigest;
  6. import org.apache.commons.codec.binary.Hex;
  7. import org.apache.commons.codec.digest.DigestUtils;
  8. import org.apache.commons.codec.digest.Md5Crypt;
  9. import org.junit.Test;
  10. /**
  11. * 简单的使用MessageDigest实现消息摘要的小案例
  12. * 收获:
  13. * 使用摘要算法获得字节数组无法直接输出,需要转成相应的十六进制才能获得结果
  14. * @author Summer
  15. *
  16. */
  17. public class Demo {
  18. private static String targetString = “I am Summer!”;
  19. /**
  20. * 测试MD5算法加密效果,转成了十六进制
  21. * JDK实现
  22. * @throws Exception
  23. */
  24. @Test
  25. public void test1() throws Exception {
  26. MessageDigest md = MessageDigest.getInstance(“MD5”);
  27. byte [] bytes = md.digest(targetString.getBytes());
  28. String result = Hex.encodeHexString(bytes);
  29. System.out.println(“MD5加密后的串是:”+result);
  30. //5b704caf20c179cdf61d7121e59dcd76
  31. }
  32. /**
  33. * 测试MD2算法加密效果,转成了十六进制
  34. * JDK实现
  35. * @throws Exception
  36. */
  37. @Test
  38. public void test2() throws Exception {
  39. MessageDigest md = MessageDigest.getInstance(“MD2”);
  40. byte [] bytes = md.digest(targetString.getBytes());
  41. String result = Hex.encodeHexString(bytes);
  42. System.out.println(“MD5加密后的串是:”+result);
  43. //387992acc0a756abc7026d635383b0a7
  44. }
  45. /**
  46. * 测试MD2算法加密效果,不转成十六进制
  47. * JDK实现
  48. * @throws Exception
  49. */
  50. @Test
  51. public void test3() throws Exception {
  52. MessageDigest md = MessageDigest.getInstance(“MD2”);
  53. byte [] bytes = md.digest(targetString.getBytes());
  54. System.out.println(bytes);
  55. }
  56. /**
  57. * 测试MD5算法加密效果,不转成十六进制
  58. * JDK实现
  59. * @throws Exception
  60. */
  61. @Test
  62. public void test4() throws Exception {
  63. MessageDigest md = MessageDigest.getInstance(“MD5”);
  64. byte [] bytes = md.digest(targetString.getBytes());
  65. System.out.println(bytes.toString());
  66. }
  67. /**
  68. * 使用commons codec的方式对目标字符串进行加密MD2,并使用十六进制进行输出
  69. * @throws Exception
  70. */
  71. @Test
  72. public void test5() throws Exception {
  73. MessageDigest md = DigestUtils.getMd2Digest();
  74. byte[] bytes = md.digest(targetString.getBytes());
  75. String result = Hex.encodeHexString(bytes);
  76. System.out.println(“Commons codec 方式加密MD2:”+ result);
  77. //387992acc0a756abc7026d635383b0a7
  78. }
  79. /**
  80. * 使用commons codec的方式对目标字符串进行加密MD5,并使用十六进制进行输出
  81. * 对比方法发现,使用CC这个开源方式,可以大大的简化操作。但其底层仍是JDK实现的,cc知识做了一些简化,仅此而已
  82. * @throws Exception
  83. */
  84. @Test
  85. public void test6() throws Exception {
  86. MessageDigest md = DigestUtils.getMd5Digest();
  87. byte[] bytes = md.digest(targetString.getBytes());
  88. String result = Hex.encodeHexString(bytes);
  89. System.out.println(“Commons codec 方式加密MD5:”+ result);
  90. //5b704caf20c179cdf61d7121e59dcd76
  91. System.out.println(DigestUtils.md5Hex(targetString));
  92. //5b704caf20c179cdf61d7121e59dcd76
  93. }
  94. }
  • 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
  • 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

SHA方式


  1. package SHA;
  2. /**
  3. * SHA: Security Hash Algorithm —–安全哈希算法
  4. */
  5. import java.security.MessageDigest;
  6. import org.apache.commons.codec.binary.Hex;
  7. import org.junit.Test;
  8. public class Demo {
  9. private static String targetString = “I am Summer!”;
  10. /**
  11. * 使用JDK方式以SHA1的方式实现消息摘要算法
  12. * @throws Exception
  13. */
  14. @Test
  15. public void testSHA1() throws Exception {
  16. /*方式一:
  17. * //算法名称对于SHA1方式: 可以是SHA也可以是SHA1
  18. MessageDigest md = MessageDigest.getInstance(“SHA”);
  19. byte[] bytes = md.digest(targetString.getBytes());
  20. String result = Hex.encodeHexString(bytes);
  21. System.out.println(“JDK 方式实现SHA1消息摘要的加密:\t”+ result);
  22. //a621ca634410c2521ef560736a6a9da048f42961
  23. */
  24. /*
  25. * 方式二:
  26. */
  27. MessageDigest md = MessageDigest.getInstance(“SHA”);
  28. md.update(targetString.getBytes());
  29. String result = Hex.encodeHexString(md.digest());
  30. System.out.println(result);
  31. }
  32. /**
  33. * 使用JDK方式以SHA-224的方式实现消息摘要算法
  34. * @throws Exception
  35. */
  36. @Test
  37. public void testSHA224() throws Exception {
  38. MessageDigest md = MessageDigest.getInstance(“SHA-224”);
  39. md.update(targetString.getBytes());
  40. String result = Hex.encodeHexString(md.digest());
  41. System.out.println(result);
  42. //6fb07533ef05a1f1b6a0fbd25f08ed3cbcf17807a507c0224756f06d
  43. }
  44. /**
  45. * 使用JDK方式以SHA-384的方式实现消息摘要算法
  46. * @throws Exception
  47. */
  48. @Test
  49. public void testSHA384() throws Exception {
  50. MessageDigest md = MessageDigest.getInstance(“SHA-384”);
  51. md.update(targetString.getBytes());
  52. String result = Hex.encodeHexString(md.digest());
  53. System.out.println(result);
  54. //c640d1c73a8e2078b290f284fce59da103ecfc1c3c9442cc90ebd5f08900d8e6f19551da7b2f213d96dd055bd2759698
  55. }
  56. /**
  57. * 使用JDK方式以SHA-256的方式实现消息摘要算法
  58. * @throws Exception
  59. */
  60. @Test
  61. public void testSHA256() throws Exception {
  62. MessageDigest md = MessageDigest.getInstance(“SHA-256”);
  63. md.update(targetString.getBytes());
  64. String result = Hex.encodeHexString(md.digest());
  65. System.out.println(result);
  66. //8305809696717b31c6765b7ba89cfd67c17ef62c79a700ae4e305fee5ebdf457
  67. }
  68. /**
  69. * 使用JDK方式以SHA-512的方式实现消息摘要算法
  70. * @throws Exception
  71. */
  72. @Test
  73. public void testSHA512() throws Exception {
  74. MessageDigest md = MessageDigest.getInstance(“SHA-512”);
  75. md.update(targetString.getBytes());
  76. String result = Hex.encodeHexString(md.digest());
  77. System.out.println(result);
  78. //520abdfdc5e0ac43c795bee9da1cac3fa2f55b8e89f1d33f9a4b83367f4b74cd5d42bfa2c6d4f68c362e64d44ea664244c03c7fd2b7bc167a489fe7129c91156
  79. }
  80. }
  • 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
  • 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

MAC方式


  1. package MAC;
  2. import javax.crypto.KeyGenerator;
  3. import javax.crypto.Mac;
  4. import javax.crypto.SecretKey;
  5. import javax.crypto.spec.SecretKeySpec;
  6. import org.apache.commons.codec.binary.Hex;
  7. import org.junit.Test;
  8. /**
  9. * MAC: Message Authentication Code ——消息验证码
  10. */
  11. public class Demo {
  12. private static String targetString = “I am Summer!”;
  13. /**
  14. * 使用MAC算法以MD5方式加密
  15. * @throws Exception
  16. */
  17. @Test
  18. public void hmacMD5() throws Exception {
  19. //初始化KeyGeerator
  20. KeyGenerator keyGenerator = KeyGenerator.getInstance(“HmacMD5”);
  21. //产生密钥
  22. SecretKey secretKey = keyGenerator.generateKey();
  23. //获得密钥
  24. byte[] key = secretKey.getEncoded();
  25. key = Hex.decodeHex(new char[]{‘S’,‘u’,‘m’,‘m’,‘e’,‘r’,‘!’});
  26. //还原密钥
  27. SecretKey restoreSecretKey = new SecretKeySpec(key, “HmacMD5”);
  28. //实例化Mac
  29. Mac mac = Mac.getInstance(restoreSecretKey.getAlgorithm());
  30. //初始化Mac
  31. mac.init(restoreSecretKey);
  32. byte[] hmacMD5Bytes = mac.doFinal(targetString.getBytes());
  33. //转成十六进制并进行输出
  34. String result = Hex.encodeHexString(hmacMD5Bytes);
  35. System.out.println(result);
  36. //8371828a9e53f04977b4d2ceb73ff506
  37. }
  38. }
  • 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
  • 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

小总结


对于MAC方式,其原理其实和前两个差不多,只不过是加入了key加密的权限。详见代码哦!

WEB应用中的SESSION详解

作为一名WEB开发程序员,对session的理解是*基础的,但是现状是WEB程序员遍地都是,随便一划拉一大把,不过估计能把session能透彻理解的人应该不是很多,起码我之前对此是知之甚少,偶然看到的一个关于session的文章,经过适当整理,特拿来与大家分享,因为时间太久,文章出处已然记不清楚,无法附上,请原作理解,若有必要我会删除,谢谢!

目 录
一、Session
二、Cookies
三、Cookies机制
四、Session机制
五、Cookies机制与Session机制的区别和联系
六、常见问题
七、Session的用法

Session是WEB上有效的信息交互手段,因其使用方便、稳定、安全、可靠而被众多WEB开发者所认知。尤其在互联网身份验证、网上电子购物等方面的应用更为广泛。下面就着重来介绍下Session。

一、Session
Session,在汉语中表示通话、会话、对话(期)、话路[对谈时间]的意思,其本来的含义一个终端用户与交互系统进行通信的时间(间隔),通常是指从注册(进入系统)到注销(退出系统)之间所经过的时间。比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个Session。有时候我们可以看到这样的话“在一个浏览器会话期间,…”,这里的会话一词用的就是这个意思,是指从一个浏览器窗口打开到关闭这个期间。Session在我们的网络应用中就是一种客户端与服务器端保持状态的解决方案,有时候Session也用来指这种解决方案的存储结构,
Session对象,就是客户端浏览器与服务器之间建立的互动信息状态。每一个不同的用户连接将得到不同的Session,也就是说Session与用户之间是一种一对一的关系。Session在用户进入网站时由服务器自动产生,并在用户正常离开站点时释放。使用Session的好处就在于,可以将很多与用户相关的信息,例如用户的帐号、昵称等保存到Session中;利用Session,可以跟踪用户在网站上的活动。例如:当你上网进入一个网站时,如果你没有登陆,无论你访问哪几个页面都会跳转回登陆页。还有就是你在购物时,不可能把你的东西放到别人的购物车里去,这就得用一个信息变量来判断!
如果能够提供一些按需生成的动态信息会使web变得更加有用,就像给有线电视加上点播功能一样。这种需求一方面迫使HTML逐步添加了表单、脚本、DOM等客户端行为,另一方面在服务器端则出现了CGI规范以响应客户端的动态请求,作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的Session机制则是又一种在客户端与服务器之间保持状态的解决方案。

二、Cookies 
Cookie是WEB上*常用的跟踪用户会话方式,当  Cookie被禁止后,一般都用URL重写来跟踪会话。Cookie是一种由服务器发送给客户的片段信息,存储在客户环境中,并在客户所有的对服务器的请求中都要发回它。就好比我们在用IE登陆某个电子购物商城时,IE在得到商品列表页面的 同时还收到Set-Cookie应答头信息,我们打开一个Cookie文件,我们所看到的格式一般都是:
Cookie:NAME=VALUE;Comment=COMMENT;Domain=DOMAINNMAM;Max-age=SECONDS;Path=PATH;secure;Version=1*DIGIT
其中NAME值对(值对间用分号分隔)是必须的,其余都是可选的。*重要的信息当然也在所必须的值对里了,VALUE是NAME的值,也是这个Cookie的标识,Max-age定义了Cookie的*长生存时间,其它几个可选值对可参阅http://www.faqs.org/rfcs/rfc2109.html。当我们选购了某种商品,向服务器发送选购清单时,会自动在你的请求信息头里加上NAME值对,如果Cookie被禁止,则用URL重写方式在URL请求地址上附加NAME值对。当Web服务器收到这个请求后,会检查该Cookie是否存在,然后相应的跟踪会话。从以上分析不难理解,其实Web服务器跟踪会话就靠Set-Cookie头信息,跟踪NAME值对进行身份验证。假如我们用非Web终端接收Web服务器的响应信息,从中解析出Cookie头信息,当再次向Web服务器发送请求时附加上解析出的Cookie信息,Web服务器据此不就可以进行身份认证了吗?
Cookies中文是饼干的意思,对于为何引用Cookies,从网上查找了一些资料:
在浏览器与WEB服务器之间是使用HTTP协议进行通信的,当某个用户发出页面请求时,WEB服务器只是简单的进行响应,然后就关闭与该用户的连接。因此当一个请求发送到WEB服务器时,无论其是否是*次来访,服务器都会把它当作*次来对待,这样的不好之处可想而知。为了弥补这个缺陷,Netscape开发出了cookie这个有效的工具来保存某个用户的识别信息,因此人们昵称为“小甜饼”。cookies是一种WEB服务器通过浏览器在访问者的硬盘上存储信息的手段:Netscape Navigator使用一个名为cookies.txt本地文件保存从所有站点接收的Cookie信息;而IE浏览器把Cookie信息保存在类似于c:\Internet 临时文件\的目录下。当用户再次访问某个站点时,服务端将要求浏览器查找并返回先前发送的Cookie信息,来识别这个用户。Cookies给网站和用户带来的好处:
(1)、Cookie能使站点跟踪特定访问者的访问次数、*后访问时间和访问者进入站点的路径
(2)、Cookie能告诉在线广告商广告被点击的次数,从而可以更精确的投放广告
(3)、Cookie有效期限未到时,Cookie能使用户在不键入密码和用户名的情况下进入曾经浏览过的一些站点
(4)、Cookie能帮助站点统计用户个人资料以实现各种各样的个性化服务,其实,cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力.

三.Cookie机制 
Cookie机制采用的是在客户端保持状态的方案。
Cookie机制,就是当服务器对访问它的用户生成了一个Session的同时服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie,保存在客户端,里面记录着用户当前的信息,当用户再次访问服务器时,浏览器检查所有存储的cookie,如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置也就是对应的Cookie文件。 若存在,则把该cookie附在请求资源的HTTP请求头上发送给服务器,例如:当我们登陆了一个网站,并且填写了有关资料,以本站会员的名义登陆上了有关网页,这时你把浏览器关闭,再重启进入该网站的某一个页面时是以你登陆过的会员进去的,当然,不是所有网站都是这样,我们知道,cookie的保存有临时性的和持久性的,大多都是临时性的,也就是cookie只保存在客户端的内存中,而没有保存在硬盘上,当关闭浏览器,cookie也就销毁。以下是有关cookie机制的一些具体说明:
cookie的内容主要包括:名字,值,过期时间,路径和域。
其中域可以指定某一个域比如.google.com,相当于总店招牌,比如宝洁公司,也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com,可以用飘柔来做比。
路径就是跟在域名后面的URL路径,比如/或者/foo等等,可以用某飘柔专柜做比。路径与域合在一起就构成了cookie的作用范围。
如果不设置过期时间,则表示这个cookie的生命期为浏览器会话期间,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里,当然这种行为并不是规范规定的。如果设置了过期时间,浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie仍然有效直到超过设定的过期时间。
存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存里的cookie,不同的浏览器有不同的处理方式。对于微软的IE浏览器,在一个打开的窗口上按Ctrl-N(或者从文件菜单)打开的窗口可以与原窗口共享,而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存cookie;对于火狐狸firefox浏览器,所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用Session机制的web应用程序开发者造成很大的困扰。

四、Session机制 
Session机制采用的是在服务器端保持状态的方案。
当用户访问到一个服务器,服务器就要为该用户创建一个SESSION,在创建这个SESSION的时候,服务器首先检查这个用户发来的请求里是否包含了一个SESSIONID,如果包含了一个SESSIONID则说明之前该用户已经登陆过并为此用户创建过SESSION,那服务器就按照这个SESSIONID把这个SESSION在服务器的内存中查找出来(如果查找不到,就有可能为他新创建一个),如果客户端请求里不包含有SESSIONID,则为该客户端创建一个SESSION并生成一个与此SESSION相关的SESSIONID。这个SESSIONID是唯一的、不重复的、不容易找到规律的字符串,这个SESSIONID将被在本次响应中返回到客户端保存,而保存这个SESSIONID的正是COOKIE,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器。
我们知道在IE中,我们可以在工具的internet选项中把COOKIE禁止,那么会不会出现把客户端的COOKIE禁止了,SESSIONID就无法再用了呢?找了一些资料说明,可以有其他机制在COOKIE被禁止时仍然能够把Session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把Session id直接附加在URL路径的后面一种是作为URL路径的附加信息,表现形式为:
http://…./xxx;jSession=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764;
另一种是作为查询字符串附加在URL后面,表现形式为:
http://…../xxx?jSession=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764
还有一种就是表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把Session id传递回服务器。这里就不介绍了。
我们常说的在一个IE被打开是创建一个Session,当关闭IE时Session也就被删除,事实上,除非程序通知服务器删除Session,否则Session会被服务器一直保留,直到Session的失效时间到了自动删除。服务器不知道IE被关闭,IE不会主动在其关闭之前通知服务器它将要关闭。程序一般都是在用户做注销时删除Session。我们产生这种错觉的原因是:一般Session机制都使用cookie来保存Session id,而一旦关闭IE浏览器,Session id就不存在了,再连接服务器时找不到原来的Session了.如果服务器设置的cookie被保存到硬盘上,或者使用某种手段改写浏览器发出的 HTTP请求头,把原来的Session id发送给服务器,则再次打开浏览器仍然能够找到原来的Session。恰恰是由于关闭浏览器不会导致Session被删除,迫使服务器为seesion设置了一个失效时间,当距离客户端上一次使用Session的时间超过这个失效时间时,服务器就可以认为客户端已经停止了活动,才会把Session删除以节省存储空间。
一般情况下,Session都是存储在内存里,当服务器进程被停止或者重启的时候,内存里的Session也会被清空,如果设置了Session的持久化特性,服务器就会把Session保存到硬盘上,当服务器进程重新启动或这些信息将能够被再次使用。

五、cookie机制与Session机制的区别和联系
具体来说cookie机制采用的是在客户端保持状态的方案,而Session机制采用的是在服务器端保持状态的方案。同时我们也看到,由于在服务器端保持状态的方案在客户端也需要保存一个标识,所以Session机制可能需要借助于cookie机制来达到保存标识的目的,但实际上还有其他选择。例如,我们经常用到的会员卡,也就相当于这种情况。消费到了一定程度就有*,就如下面例子说明:
1.发给顾客一张卡片,上面记录着消费的数量,一般还有个有效期限。每次消费时,如果顾客出示这张卡片,则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。
2、发给顾客一张会员卡,除了卡号之外什么信息也不纪录,每次消费时,如果顾客出示该卡片,则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。
以下是一些关于两者的区别与联系:
具体来说cookie机制采用的是在客户端保持状态的方案。它是在用户端的会话状态的存贮机制,他需要用户打开客户端的cookie支持。cookie的作用就是为了解决HTTP协议无状态的缺陷所作的努力.而Session机制采用的是一种在客户端与服务器之间保持状态的解决方案。同时我们也看到,由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以Session机制可能需要借助于cookie机制来达到保存标识的目的。而Session提供了方便管理全局变量的方式。
Session是针对每一个用户的,变量的值保存在服务器上,用一个Session来区分是哪个用户Session变量,这个值是通过用户的浏览器在访问的时候返回给服务器,当客户禁用cookie时,这个值也可能设置为由get来返回给服务器。
就安全性来说:当你访问一个使用Session 的站点,同时在自己机子上建立一个cookie,建议在服务器端的SESSION机制更安全些.因为它不会任意读取客户存储的信息。
正统的cookie分发是通过扩展HTTP协议来实现的,服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。
从网络服务器观点看所有HTTP请求都独立于先前请求。就是说每一个HTTP响应完全依赖于相应请求中包含的信息.
状态管理机制克服了HTTP的一些限制并允许网络客户端及服务器端维护请求间的关系。在这种关系维持的期间叫做会话(Session)。
Cookies是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器。IETF RFC 2965 HTTP State Management Mechanism 是通用cookie规范。网络服务器用HTTP头向客户端发送cookies,在客户终端,浏览器解析这些cookies并将它们保存为一个本地文件,它会自动将同一服务器的任何请求缚上这些cookies 。
来看一下Tomcat是如何实现web应用程序之间session的隔离的,从Tomcat设置的cookie路径来看,它对不同的应用程序设置的cookie路径是不同的,这样不同的应用程序所用的session id是不同的,因此即使在同一个浏览器窗口里访问不同的应用程序,发送给服务器的session id也可以是不同的。

六、常见问题 
1、Session在何时被创建
Session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page Session=”false”%> 关闭Session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession Session = HttpServletRequest.getSession(true);这也是JSP中隐含的Session对象的来历。由于Session会消耗内存资源,因此,如果不打算使用Session,应该在所有的JSP中关闭它。
2、Session何时被删除
综合前面的讨论,Session在下列情况下被删除:
a.程序调用HttpSession.invalidate();
b.距离上一次收到客户端发送的Session id时间间隔超过了Session的超时设置;
c.服务器进程被停止(非持久Session)。
3、如何做到在浏览器关闭时删除Session
严格的讲,做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作,然后向服务器发送一个请求来删除Session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。
4、有个HttpSessionListener是怎么回事
你可以创建这样的listener去监控Session的创建和销毁事件,使得在发生这样的事件时你可以做一些相应的工作。注意是Session的创建和销毁动作触发listener,而不是相反。类似的与HttpSession有关的listener还有HttpSessionBindingListener,HttpSessionActivationListener和 HttpSessionAttributeListener。
5、存放在Session中的对象必须是可序列化的吗
不是必需的。要求对象可序列化只是为了Session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把Session交换出内存。在Weblogic Server的Session中放置一个不可序列化的对象在控制台上会收到一个警告。
6、开两个浏览器窗口访问应用程序会使用同一个Session还是不同的Session
对Session来说是只认id不认人,因此不同的浏览器,不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。
7、如何防止用户打开两个浏览器窗口操作导致的Session混乱
这个问题与防止表单多次提交是类似的,可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端,同时保存在Session里,客户端提交表单时必须把这个id也返回服务器,程序首先比较返回的id与保存在Session里的值是否一致,如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口,一般不设置这个id,或者使用单独的id,以防主窗口无法操作,建议不要再window.open打开的窗口里做修改操作,这样就可以不用设置。
8、为什么Session不见了
排除Session正常失效的因素之外,服务器本身的可能性应该是微乎其微的;理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。出现这一问题的大部分原因都是程序的错误,*常见的就是在一个应用程序中去访问另外一个应用程序。
9、服务器关掉后,当前Session会丢掉吗
这个取决于你使用什么样的web服务器以及web服务器是如何配置的。tomcat在shutdown前默认会自动将Session保存到指定的目录中,重新启动是重新加载,因此tomcat重新启动后,Session是可以继续使用的。此外,你还何以将Session保存到数据库中,这个要在server.xml中配置。
10、Cookie的过期和Session的超时有什么区别
会话的超时由服务器来维护,它不同于Cookie的失效日期。首先,会话一般基于驻留内存的cookie不是持续性的cookie,因而也就没有截至日期。即使截取到JSESSION cookie,并为它设定一个失效日期发送出去。浏览器会话和服务器会话也会截然不同。

七、Session的用法 
Session 对象
可以使用 Session 对象存储特定用户会话所需的信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。
当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。
Session 对象*常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。
注意:会话状态仅在支持 cookie 的浏览器中保留。
语法
Session.collection|property|method
集合
Contents 包含已用脚本命令添加到会话中的项目。
StaticObjects 包含通过 <OBJECT> 标记创建的并给定了会话作用域的对象。
属性
CodePage 将用于符号映射的代码页。
LCID 现场标识。
SessionID 返回用户的会话验证。
Timeout 应用程序会话状态的超时时限,以分钟为单位。
方法
Abandon 该方法破坏 Session 对象并释放其资源。
事件
asa 文件中声明下列事件的脚本。
Session_OnEnd
Session_OnStart
注释

您可以在 Session 对象中存储值。存储在 Session 对象中的信息在会话及会话作用域内有效。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速