globaltable(globaltablebratchtable)
servlet使用的会话跟踪除session外还有哪些方式
HTTP是一种无连接的协议,如果一个客户端只是单纯地请求一个文件(HTML或GIF),服务器端可以响应给客户端,并不需要知道一连串的请求是否来自于相同的客户端,而且也不需要担心客户端是否处在连接状态。但是这样的通信协议使得服务器端难以判断所连接的客户端是否是同一个人。当进行Web程序开发时,我们必须想办法将相关的请求结合一起,并且努力维持用户的状态在服务器上,这就引出了会话追踪(session tracking)。
1:会话与会话追踪
session中文经常翻译为“会话”,其本来的含义是指有始有终的一系列动作或消息,比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session。有时候可以看到这样的话“在一个浏览器会话期间……”,这里的会话一词用的就是其本义,是指从一个浏览器窗口打开到关闭这个期间;如果说“用户在一次会话期间……”这样一句话,它指用户的一系列动作,比如从登录到选购商品到结账登出这样一个网上购物的过程;然而有时候也可能仅仅是指一次连接。session的含义很多,其中的差别只能靠上下文来推断。session tracking(会话追踪)是指一类用来在客户端与服务器之间保持状态的解决方案,简单地说,当一个客户在多个页面间切换时,服务器会保存该用户的信息。
2:实现会话追踪的4种方式会话追踪的实现方式有下列4种方式:
(1)使用持续Cookies(Persistent Cookies)。
(2)重写包含额外参数的URL(URL Rewriting)。
(3)建立含有数据的隐藏表单字段(Hidden Form Field)。
(4)使用内建session对象。
前三种会话追踪方式是传统的做法,每种做法都有缺点。最后一种方法是目前最常用,也是最有效的解决方案,因此在这里将把讨论重心放在第4种会话追踪方式上,然而为求彻底了解会话追踪的机制,还是先将传统的会话追踪方式先做一番介绍。(这里和我的理解不太一样,记录下我的理解,Session的机制是 Java Servlet 规范规定的,而tomcat container实现了这个规定,tomcat 是通过cookie 和 url rewriting的方式来实现的,也就是通过cookie 或url rewriting 保存一个seesionID, 这样在内部tomcat 把这个sesionID和一个Map联系起来,达到将变量和session联系起来的目的。所以第一和第二中方法是tomcat或其他servlet container实现session机制的手段,当然我们也可以自己实现。而第三种方法只是在两个页面跳转时传递变量的一种方式,要想用这种方式实现sesion机制还是不太现实,要每个页面都写下hidden数据,而且要写下所有要传的变量。)
2.1:使用Cookie
Cookie是一个小小的文本文件,它是将会话信息记录在这个文本文件内,每个页面都去Cookie中提取以前的会话信息。例如:
String sessionID = makeUniqueString();
HashMap sessionInfo = new HashMap();
HashMap globalTable = findTableStoringSessions();
globalTable.put(sessionID, sessionInfo);
Cookie sessionCookie =new Cookie("JSESSIONID", sessionID);
sessionCookie.setPath("/");
response.addCookie(sessionCookie);
上面这段代码先将会话信息记录在HashMap中,保存在服务器端,并用sessionID标识,然后把sessionID保存在名为“JSESSIONID”的Cookie中。
Cookie[] cookies = request.getCookies();
String sessionid = null;
HashMap sessionInfo = null;
HashMap globalTable = findTableStoringSessions();
if(cookies!=null){
for(int i=0;icookies.length;i++){
if(cookies[i].getName().equals("JSESSIONID")){
sessionid = cookies[i].getValue();
break;
}
}
if(sessionid!=null){
sessionInfo = globalTable.get(sessionid);
//We can use the sessionInfo to get value that we want
}
}
用户请求到达服务器后,先从Cookie中取出sessionID,然后从HashMap中取出会话信息。这样就实现了会话追踪。
虽然Cookie强大且持续性高,但是由于有些用户因为担心Cookie对个人隐私的威胁,会关闭Cookie,一旦如此,便无法利用Cookie来达到会话追踪的功能。
2.2:URL重写
URL重写是利用GET的方法,在URL的尾部添加一些额外的参数来达到会话追踪(session tracking)的目的,服务器将这个标识符与它所存储的有关会话的数据关联起来。URL看起来如下:
;jsessionid=1234,
使用URL重写的优点是Cookie被禁用或者根本不支持的情况下依旧能够工作。但也有很多缺点:
1. 必须对所有指向您的网站的URL进行编码。
2. 所有页面必须动态生成。
3. 不能使用预先记录下来的URL进行访问,或者从其他网站链接进行访问。
2.3:隐藏表单字段
隐藏表单字段的方法,是利用HTML内hidden的属性,把客户端的信息,在用户不察觉的情形下,偷偷地随着请求一起传送给到服务器处理,这样一来,就可以进行会话跟踪的任务了。可以下列的方法来做隐藏表单字段的会话追踪。
input type="hidden" name="userID" value="15"
然后将重要的用户信息,如ID之类独一无二的数据,以隐藏字段的方式传送给服务器。隐藏字段的优点在于session数据传送到服务器端时,并不象GET的方法,会将session数据保露在URL之上。不过这种做法还是有它的缺点:一旦session数据储存在隐藏字段中,就仍然有暴露数据的危机,因为只要用户直接观看HTML的源文件,session数据将会暴露无疑。这将造成安全上的漏洞,特别当用户数据是依赖于用户ID、密码来取得的时候,将会有被盗用的危险。另外这种方法只适用特定的一个流程,不适用于通常意义的会话跟踪。
2.4:使用内建session对象
传统的会话追踪方式使用比较麻烦,Servlet的会话机制基于Cookie或URL重写技术,融合了这两种技术的优点。当客户端允许使用Cookie时,内建session对象使用Cookie进行会话追踪;如果客户端禁用Cookie,则选择使用URL重写。
(1)获取session对象例如把购物车作为属性存储在session中,在其他JSP页面中可以通过session再获得购物车。
// 在JSP页面中可以直接使用session
ShoppingCart cart = (ShoppingCart)session.getAttribute("cart");
内建的session对象是javax.servlet.http.HttpSession类的实例,如果在JavaBean或者Servlet中使用session就需要先从当前的request对象中取得,例如:
// 得到用户session和购物篮
HttpSession session = request.getSession();
ShoppingCart cart = (ShoppingCart)session.getAttribute("cart");
(2)读写session中的数据向session中存入对象使用setAttribute方法,通过getAttribute方法读取对象。从session返回的值注意要转换成合适的类型,要注意检查结 果是否为null。例如下面一段代码:
HttpSession session = request.getSession();
SomeClass value = (SomeClass)session.getAttribute("someID");
if (value == null) {
value = new SomeClass(...);
session.setAttribute("someID", value);
}
doSomethingWith(value);
(3)废弃session数据调用removeAttribute废弃session中的值,即移除与名称关联的值。
调用invalidate废弃整个session,即废弃当前的session。
如果用户注销离开站点,注意废弃与用户相关联的所有session。
(4)session的生命周期由于没有办法知道HTTP客户端是否不再需要session,因此每个session都关联一个时间期限使它的资源可以被回收。setMaxInactiveInterval(int secondsToLive)
(5)服务器使用session时,默认使用Cookie技术进行会话追踪,通常,会话管理是通过服务器将 Session ID 作为一个 cookie 存储在用户的 Web 浏览器中,并用它来唯一标识每个用户会话。如果客户端不接受Cookie的时候,服务器可以利用URL重写的方式将sessionID作为参数附在URL后面,来实现会话管理。
当我们在进行forward,redirect时,一定要调用下边两个方法,以保持session一直有效(如果不调用的话,那么你到了新的页面时session就失效了,因为session ID没有传过来)
Servlet中Interface HttpServletResponse 规定了两个方法,response.encodeURL()或response.encodeRedirectURL()方法,这两个方法首先判断Cookies是否被浏览器支持;如果支持,则参数URL被原样返回,session ID将通过Cookies来维持;否则返回带有sessionID的URL。Tomcat服务器实现了这两个方法。
下面是使用encodeURL方法的示例,两个文件hello1.jsp和hello2.jsp。
a: hello1.jsp的完整程序代码如下:
%@ page contentType="text/html;charset=gb2312"%
%String url =response.encodeURL("hello2.jsp");%
a href='%=url%'进入到hello2.jsp/a
b: 解释:
hello1.jsp利用了response对象内的encodeURL方法,将URL做了一个编码动作。编码不是这里关心的重点,重点是如果浏览器的cookie被禁用的话那么象;jsessionid=A09F3A5583825EE787580106CC62A1E8 这样字符串就会被添加到 hello2.jsp 的后面,也就是告诉了下一个页面session的信息。
b: 若要使用重定向,例如:
response.sendRedirect("hello2.jsp");也应该改为:response.sendRedirect(response.encodeRedirectURL("hello2.jsp"));同时需要注意的是,将session的ID以URL的编码方式进行时,需将每一页都编码,才能保留住session的ID。如果遇到没有编码的URL,则无法进行会话跟踪。
c: hello2.jsp的完整程序代码如下:
%@ page contentType="text/html;charset=gb2312"%
% out.println("sessionID is "+session.getId());%
d: 可以看到如果服务器使用URL重写,它将会话信息附加到URL上,如下所示:
;jsessionid=A09F3A5583825EE787580106CC62A1E8
e: 实质上 URL 重写是通过向 URL 连接添加参数,并把 session ID 作为值包含在连接中,以便应用服务器可以根据sessionID从cache中的取回session.
TOMCAT服务器
SESSION实现会话跟踪通常是cookie和url重写,如果浏览器不禁止cookie的话,tomcat优先使用cookie实现,否则它将使用URL重写来支持SESSION.
URL重写的额外数据是服务器自动添加的,那么服务器是怎么添加的呢?Tomcat在返回Response的时候,检查JSP页面中所有的URL,包括所有的链接,和 Form的Action属性,在这些URL后面加上“;jsessionid=xxxxxx”。添加url后缀的代码片段如下:
org.apache.coyote.tomcat5.CoyoteResponse类的toEncoded()方法支持URL重写。
1 StringBuffer sb = new StringBuffer(path);
2 if( sb.length() 0 ) { // jsessionid can't be first.
3 sb.append(";jsessionid=");
4 sb.append(sessionId);
5 }
6 sb.append(anchor);
7 sb.append(query);
8 return (sb.toString());
如何取得到 JSESSIONID COOKIE
有下列4种方式:
(1)使用持续Cookies(Persistent Cookies)。
(2)重写包含额外参数的URL(URL Rewriting)。
(3)建立含有数据的隐藏表单字段(Hidden Form Field)。
(4)使用内建session对象。
前三种会话追踪方式是传统的做法,每种做法都有缺点。最后一种方法是目前最常用,也是最有效的解决方案,因此在这里将把讨论重心放在第4种会话追踪方式上,然而为求彻底了解会话追踪的机制,还是先将传统的会话追踪方式先做一番介绍。
2.1:使用Cookie
Cookie是一个小小的文本文件,它是将会话信息记录在这个文本文件内,每个页面都去Cookie中提取以前的会话信息。例如:
String sessionID = makeUniqueString();
HashMap sessionInfo = new HashMap();
HashMap globalTable = findTableStoringSessions();
globalTable.put(sessionID, sessionInfo);
Cookie sessionCookie =new Cookie("JSESSIONID", sessionID);
sessionCookie.setPath("/");
response.addCookie(sessionCookie);
上面这段代码先将会话信息记录在HashMap中,保存在服务器端,并用sessionID标识,然后把sessionID保存在名为“JSESSIONID”的Cookie中。
用户请求到达服务器后,先从Cookie中取出sessionID,然后从HashMap中取出会话信息。这样就实现了会话追踪。
虽然Cookie强大且持续性高,但是由于有些用户因为担心Cookie对个人隐私的威胁,会关闭Cookie,一旦如此,便无法利用Cookie来达到会话追踪的功能。
hive 什么样的operator会导致生成一个mapreduce任务
Hive中在做多表关联时,由于Hive的SQL优化引擎还不够强大,表的关联顺序不同往往导致产生不同数量的MapReduce作业数。这时就需要通过分析执行计划对SQL进行调整,以获得最少的MapReduce作业数。举一个例子(案例基于Hive 0.6.0):
create table ljn1(
k1 bigint,
k2 String,
v1 int
);
create table ljn2(
k1 bigint,
v2 int
);
create table ljn3(
k1 bigint,
v3 int
);
create table ljn4(
k1 bigint,
v4 int
);
create table ljn5(
k1 bigint,
v5 int
);
create table ljn6(
k2 string,
v6 int
);
然后看一下下面这个SQL的执行计划:
explain
select a.v1
from
ljn1 a
left outer join ljn2 b on (a.k1 = b.k1)
left outer join ljn3 c on (a.k1 = c.k1)
left outer join ljn4 d on (a.k1 = d.k1)
left outer join ljn6 e on (a.k2 = e.k2)
left outer join ljn5 f on (a.k1 = f.k1);
STAGE DEPENDENCIES:
Stage-5 is a root stage
Stage-1 depends on stages: Stage-5
Stage-2 depends on stages: Stage-1
Stage-0 is a root stage
STAGE PLANS:
Stage: Stage-5
Map Reduce
Alias - Map Operator Tree:
a
TableScan
alias: a
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 0
value expressions:
expr: k1
type: bigint
expr: k2
type: string
expr: v1
type: int
b
TableScan
alias: b
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 1
Reduce Operator Tree:
Join Operator
condition map:
Left Outer Join0 to 1
condition expressions:
0 {VALUE._col0} {VALUE._col1} {VALUE._col2}
1
handleSkewJoin: false
outputColumnNames: _col0, _col1, _col2
File Output Operator
compressed: true
GlobalTableId: 0
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
Stage: Stage-1
Map Reduce
Alias - Map Operator Tree:
$INTNAME
Reduce Output Operator
key expressions:
expr: _col0
type: bigint
sort order: +
Map-reduce partition columns:
expr: _col0
type: bigint
tag: 0
value expressions:
expr: _col1
type: string
expr: _col2
type: int
c
TableScan
alias: c
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 1
d
TableScan
alias: d
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 2
f
TableScan
alias: f
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 3
Reduce Operator Tree:
Join Operator
condition map:
Left Outer Join0 to 1
Left Outer Join0 to 2
Left Outer Join0 to 3
condition expressions:
0 {VALUE._col3} {VALUE._col4}
1
2
3
handleSkewJoin: false
outputColumnNames: _col3, _col4
File Output Operator
compressed: true
GlobalTableId: 0
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
Stage: Stage-2
Map Reduce
Alias - Map Operator Tree:
$INTNAME
Reduce Output Operator
key expressions:
expr: _col3
type: string
sort order: +
Map-reduce partition columns:
expr: _col3
type: string
tag: 0
value expressions:
expr: _col4
type: int
e
TableScan
alias: e
Reduce Output Operator
key expressions:
expr: k2
type: string
sort order: +
Map-reduce partition columns:
expr: k2
type: string
tag: 1
Reduce Operator Tree:
Join Operator
condition map:
Left Outer Join0 to 1
condition expressions:
0 {VALUE._col10}
1
handleSkewJoin: false
outputColumnNames: _col10
Select Operator
expressions:
expr: _col10
type: int
outputColumnNames: _col0
File Output Operator
compressed: true
GlobalTableId: 0
table:
input format: org.apache.hadoop.mapred.TextInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Stage: Stage-0
Fetch Operator
limit: -1
常规来讲,这个SQL非常简单,a表是主表,与其他表左外关联用到了k1和k2两个关联键,使用两个MapReduce作业完全可以搞定。但是这个SQL的执行计划却给出了3个作业:(Stage-0用做数据的最终展示,该作业可以忽略不计)第1个作业(Stage-5)是a表与b表关联;第2个作业(Stage-1)是第1个作业的中间结果再与c、d、f三表关联;第3个作业(Stage-2)是第2个作业的中间结果再与e表关联。
有点搞不懂了吧,第1和第2个作业明明可以合并在一起来完成的呀!其实我也搞不懂,从执行计划中看不出原由。而且如果这个SQL去掉c或者e其中的一个关联表,第1和第2个作业就可以合并在一起!很奇妙,我没有深入探究,应该是Hive的规则优化器还不够完美。
总之,遇到这种多表关联的情况一定要记得看一下执行计划,看看Hive是不是生成了多余的作业。如果Hive真的犯傻生成了多余的作业,就要尝试改变一下SQL的写法。通常是将关联键相同的表放在一起,如果还不行就再引入子查询。例如上面这个例子改为如下SQL就可以只生成2个作业了:
explain
select t.v1
from
(
select a.k2,a.v1
from
ljn1 a
left outer join ljn2 b on (a.k1 = b.k1)
left outer join ljn3 c on (a.k1 = c.k1)
left outer join ljn4 d on (a.k1 = d.k1)
left outer join ljn5 f on (a.k1 = f.k1)
) t
left outer join ljn6 e on (t.k2 = e.k2)
;
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-2 depends on stages: Stage-1
Stage-0 is a root stage
STAGE PLANS:
Stage: Stage-1
Map Reduce
Alias - Map Operator Tree:
t:a
TableScan
alias: a
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 0
value expressions:
expr: k2
type: string
expr: v1
type: int
t:b
TableScan
alias: b
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 1
t:c
TableScan
alias: c
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 2
t:d
TableScan
alias: d
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 3
t:f
TableScan
alias: f
Reduce Output Operator
key expressions:
expr: k1
type: bigint
sort order: +
Map-reduce partition columns:
expr: k1
type: bigint
tag: 4
Reduce Operator Tree:
Join Operator
condition map:
Left Outer Join0 to 1
Left Outer Join0 to 2
Left Outer Join0 to 3
Left Outer Join0 to 4
condition expressions:
0 {VALUE._col1} {VALUE._col2}
1
2
3
4
handleSkewJoin: false
outputColumnNames: _col1, _col2
Select Operator
expressions:
expr: _col1
type: string
expr: _col2
type: int
outputColumnNames: _col0, _col1
File Output Operator
compressed: true
GlobalTableId: 0
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
Stage: Stage-2
Map Reduce
Alias - Map Operator Tree:
$INTNAME
Reduce Output Operator
key expressions:
expr: _col0
type: string
sort order: +
Map-reduce partition columns:
expr: _col0
type: string
tag: 0
value expressions:
expr: _col1
type: int
e
TableScan
alias: e
Reduce Output Operator
key expressions:
expr: k2
QTP中Global Table(全局表)中的数据可以被所有Action调用吗?
可以的,使用datatable.value("A", dtglobalsheet)即可。
事实上,一个Action2可以调用另一个Action1的表,使用datatable.value("A", "Action1")即可。
MySQL 优化之 table_open_cache
表文件描述符的缓存大小
( 当打开一个表后 会把这个表的文件描述符缓存下来 )
查看 table_open_cache
show global variables like 'table_open_cache';
设置 table_open_cache
set global table_open_cache = 2048; (立即生效重启后失效)
MySQL 配置文件 my.cnf 中 mysqld 下添加 table_open_cache
[mysqld]
table_open_cache = 2048
table_open_cache 设置多少合适呢 ?
不是越大越好 table_open_cache过大占用大量文件描述符资源而不释放
用尽了系统文件描述符资源导致无法接入新的连接
如何判断 table_open_cache 大小是否够用?
可根据MySQL的两个状态值来分析
通过以上两个值来判断 table_open_cache 是否到达瓶颈
当缓存中的值 open_tables 临近到了 table_open_cache 值的时候
说明表缓存池快要满了 但 Opened_tables 还在一直有新的增长 这说明你还有很多未被缓存的表
这时可以适当增加 table_open_cache 的大小
MySQL 5.7 参考手册 - table_open_cache