Archive for 12月 2009
glassfish 介绍 Leave a comment
谈论 数据库的隔离级别 Leave a comment
引用
一致和并发是对立的,需要根据应用,选择权宜之计
数据不一致的现象
—事务内单SQL的情况—
1.脏读-Dirty Read:本事务读取了其它事务尚未提交的修改数据
例子:读了不该读的
1:00 x=1
1:01 A用户 Update x=2(但未commit)
1:02 B用户 Select x –> x=2
合理的情况是x仍然等于1—事务内多SQL的情况(典型的如,先查再改)—
2.不可重复读-Non Repeatable Read
例子1:自相矛盾
1:00 x=1 y=2
1:01 B用户 Select x,y –> x=1 y=2
1:02 A用户 Update x=2; Commit;
1:03 B用户 Select x+y –> x+y=4
首先这个结果从单条SQL的角度看,是没有问题的。但是,如果把B的两次查询看作一个整体(事务),那么合理的情况应该是
x+y仍然等于3
或者B再进行一次事务,得出 x=2 y=2 x+y=4 的结果例子2:更新丢失
1:00 x=1
1:01 B用户 Select x –> x=1
1:02 A用户 Select x –> x=1
1:03 A用户 Update x=2; Commit;
1:04 B用户 Update x=3; Commit;
同样,从单条SQL来讲,没有任何问题。
但是从逻辑的合理性讲,一般的更新操作都是先查再改,换言之
A真正想做的是Update x from 1 to 2
B真正想做的是Update x from 1 to 3
但最终却造成了在B不知情的情况下,把B的初衷改为了Update x from 2 to 33.幻影读-Phantom Read
例子:读到了未来
1:00 X1=1 X2=2
1:01 B用户 Select Xi –> X1=1 X2=2
1:02 A用户 Insert X3=3; Commit;
1:03 B用户 Select sum(Xi) –> re=6
其实道理和之前的不可重复读相同,只不过是由Insert引起的罢了。
(甚至Delete也会引起类似的问题,但好像学术界并没有对Delete进行讨论)Isolation Level
Read Uncommitted:1,2,3都会发生
Oracle中严格禁止脏读
在SQL Server 7.0中,是可以选择该级别的
Read Committed:发生2,3(Oracle的默认级别)
Repeatable Read:发生3
Serializable:都不发生Oracle的实现方式
Read Committed:默认就实现
Repeatable Read:
1. 悲观锁(select … for update),影响并发
2. 乐观锁(update where 所有字段都作为条件),不影响并发
Serializable:
alter session set isolation_level=serializable/read only数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现.在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低.
大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle.
少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎
即使是最低的级别,也不会出现 第一类 丢失 更新问题 .
1. 脏读(事务没提交,提前读取):脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
2. 不可重复读(两次读的不一致) :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
4.第一类更新丢失(回滚丢失):
当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销。也就是说第一个事务做的跟新丢失了。
5.第二类更新丢失(覆盖丢失):
第二类更新丢失实在实际应用中经常遇到的并发问题,他和不可重复读本质上是同一类并发问题,通常他被看做不可重复读的特例:当2个或这个多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新。因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的跟新…
补充 : 基于元数据的 Spring 声明性事务 :Isolation 属性一共支持五种事务设置,具体介绍如下:
l DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .
l READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )
l READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行)
l REPEATABLE_READ 会出幻读(锁定所读取的所有行)
l SERIALIZABLE 保证所有的情况不会发生(锁表)
不可重复读的重点是修改 :
同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 , 第 1 次和第 2 次读出来的记录数不一样
JDBC大数据处理和数据库隔离级别 Leave a comment
一段对Date类型数据数据库存储的操作
这的Date是java.util.Date类型,后面对数据库的Date操作的是java.sql.Date。由于java.sql.Date是java.util.Date的子类,所以无法把java.util.Date对象直接赋值给java.sql.Date(父类不能代替子类)
static void create(String name, Date birthday, float money)
throws SQLException {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 2.建立连接
conn = JdbcUtils.getConnection();
// conn = JdbcUtilsSing.getInstance().getConnection();
// 3.创建语句
String sql = "insert into user(name,birthday, money) values (?, ?, ?) ";
ps = conn.prepareStatement(sql);
ps.setString(1, name);
传入的birthday是java.util.Date类型,这里把它转化成它的子类java.sql.Date类型
ps.setDate(2, new java.sql.Date(birthday.getTime()));
ps.setFloat(3, money);
// 4.执行语句
int i = ps.executeUpdate();
System.out.println("i=" + i);
} finally {
JdbcUtils.free(rs, ps, conn);
}
}
大文本文件数据库读写
数据库中的varchar类型最大只能有255个字符,如果遇上需要保存大的文本文件,就需要使用数据库中的text类型字段。
CLOB text
存:ps.setCharacterStream(index, reader, length);
ps.setString(i, s);
取:reader = rs. getCharacterStream(i);
reader = rs.getClob(i).getCharacterStream();
string = rs.getString(i);
关于大文件数据库读写的操作方式:
从外界读入数据进数据库
// 2.建立连接
conn = JdbcUtils.getConnection();
// conn = JdbcUtilsSing.getInstance().getConnection();
// 3.创建语句
String sql = "insert into clob_test(big_text) values (?) ";
ps = conn.prepareStatement(sql);
获得需要输入的文件的对象
File file = new File("src/cn/itcast/jdbc/JdbcUtils.java");
把文件转化成reader流对象
Reader reader = new BufferedReader(new FileReader(file));
把这个流对象保存进数据库
ps.setCharacterStream(1, reader, (int) file.length());
// ps.setString(1, x);
// 4.执行语句
int i = ps.executeUpdate();
关闭流
reader.close();
从数据库中读取text文件
rs = st.executeQuery("select big_text from clob_test");
从数据库中获得保存大文件的字段对象
Clob clob = rs.getClob(1);
获得这个字段中数据的Reader流对象
Reader reader = clob.getCharacterStream();
// reader = rs.getCharacterStream(1);
// String s = rs.getString(1);
创建一个文件和一个Writer流,把Reader中的数据全部写入到这个文件
File file = new File("JdbUtils_bak.java");
Writer writer = new BufferedWriter(new FileWriter(file));
char[] buff = new char[1024];
每次从reader流对象中读取数据,返回的i就是读取到的字符个数,如果i=0,就是读完了
for (int i = 0; (i = reader.read(buff)) > 0;) {
由于读取到最后一次的时候buff数组可能不满,这么做可以保证每次写入的数据都是有效的
writer.write(buff, 0, i);
}
writer.close();
reader.close();
对字节流的读取
当所需要读取的文件是个图片或者java的包,这个时候文本的读取方式就不行了,只能使用字节流来读取。数据库中对应保存这种数据的数据类型是BLOB
.BLOB blob
存:ps.setBinaryStream(i, inputStream, length);
取:rs.getBinaryStream(i);
rs.getBlob(i).getBinaryStream();
下面是对于字节流数据库存取操作
// 2.建立连接
conn = JdbcUtils.getConnection();
// conn = JdbcUtilsSing.getInstance().getConnection();
// 3.创建语句
String sql = "insert into blob_test(big_bit) values (?) ";
ps = conn.prepareStatement(sql);
File file = new File("IMG_0002.jpg");
这里是二进制文件,所以必须使用这个,不能使用read了
InputStream in = new BufferedInputStream(new FileInputStream(file));
ps.setBinaryStream(1, in, (int) file.length());
// 4.执行语句
int i = ps.executeUpdate();
in.close();
从数据库中读出二进制文件
// Blob blob = rs.getBlob(1);
// InputStream in = blob.getBinaryStream();
从数据库中读取文件的流对象
InputStream in = rs.getBinaryStream("big_bit");
File file = new File("IMG_0002_bak.jpg");
OutputStream out = new BufferedOutputStream(
new FileOutputStream(file));
byte[] buff = new byte[1024];
for (int i = 0; (i = in.read(buff)) > 0;) {
out.write(buff, 0, i);
}
out.close();
in.close();
工厂模式经典代码
public class DaoFactory {
private static UserDao userDao = null;
private static DaoFactory instance = new DaoFactory();
private DaoFactory() {
try {
Properties prop = new Properties();
注意这里读取配置文件的方法,使用ClassLoader来获得配置文件的输入流对象,只要这个文件在这个ClassLoader所能控制的目录结构里,就一定能够找到它。我们这里使用的类加载器可以加载到任何在classpath中的类和文件
InputStream inStream = DaoFactory.class.getClassLoader()
.getResourceAsStream("daoconfig.properties");
prop.load(inStream);
String userDaoClass = prop.getProperty("userDaoClass");
Class clazz = Class.forName(userDaoClass);
userDao = (UserDao) clazz.newInstance();
} catch (Throwable e) {
throw new ExceptionInInitializerError(e);
}
}
public static DaoFactory getInstance() {
return instance;
}
public UserDao getUserDao() {
return userDao;
}
}
事务(ACID)
原子性(atomicity):组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。
一致性(consistency):在事务处理执行前后,数据库是一致的(数据库数据完整性约束)。
隔离性(isolcation):一个事务处理对另一个事务处理的影响。
持续性(durability):事务处理的效果能够被永久保存下来 。
connection.setAutoCommit(false);//打开事务。不使用事务的自动提交
connection.commit();//提交事务。
connection.rollback();//回滚事务。
一段事务相关的代码
try {
conn = JdbcUtils.getConnection();
开启事务,把事务的自动提交关闭
conn.setAutoCommit(false);
设置隔离级别 conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
st = conn.createStatement();
String sql = "update user set money=money-10 where id=1";
st.executeUpdate(sql);
sql = "select money from user where id=2";
rs = st.executeQuery(sql);
float money = 0.0f;
if (rs.next()) {
money = rs.getFloat("money");
}
if (money > 400)
对于不知道该如何处理的异常都用运行时异常抛出
throw new RuntimeException("已经超过最大值!");
sql = "update user set money=money+10 where id=2";
st.executeUpdate(sql);
提交事务
conn.commit();
} catch (SQLException e) {
if (conn != null)
发生异常的时候事务回滚
conn.rollback();
throw e;
} finally {
JdbcUtils.free(rs, st, conn);
}
事务中的保存点
当只想撤销事务中的部分操作时可使用SavePoint
SavePoint sp = connection.setSavepoint();
如果rollerbak中没有参数,就把整个事务回滚,如果传入了一个保存点SavePoint 对象,保存点以前部分不会被回滚
connection.rollerbak(sp);connection.commit();
隔离级别多线程并发读取数据时的正确性
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
V:可能出现,X:不会出现
脏读:读到用户没有被事务提交的数据,
不可重复读:在同一个事务的读取过程中,数据发生变化,比如第一次读以后,别的线程插入新的数据,所以下次读取的时候发现数据发生变化。
可重复读:在一个事务读取数据库过程中,即便另外一个线程修改了数据库并且提交了。在当前这个事务没有关闭前,每次读取到的数据都是一样的,不会受另外一个线程提交的数据影响。
幻读:在“可重复读”情况下,有时候会出现:在Thread1中开启事务,当另外一个线程Thread2事务已经关闭,但是利用数据库本身默认设置的自动提交,在没有开启事务的情况下往数据库中修改数据,这种修改也可以成功。这个时候,Thread1线程有时候就会读到这种数据,这就是幻读。
如果数据库是“Serializable可串行化”的话,假设在Thread1一个事务开启以后,另外一个线程Thread2线程如果想修改数据库中的数据,这个Thread2修改线程会挂起无法完成,直到Thread1线程事务提交以后,Thread2的修改操作才能完成。
“Serializable可串行化”这种级别一次只准许一个人修改数据,其他线程只能查询,但是不能进行任何修改。
数据库的隔离级别 Leave a comment
一致和并发是对立的,需要根据应用,选择权宜之计
数据不一致的现象
—事务内单SQL的情况—
1.脏读-Dirty Read:本事务读取了其它事务尚未提交的修改数据
例子:读了不该读的
1:00 x=1
1:01 A用户 Update x=2(但未commit)
1:02 B用户 Select x –> x=2
合理的情况是x仍然等于1
—事务内多SQL的情况(典型的如,先查再改)—
2.不可重复读-Non Repeatable Read
例子1:自相矛盾
1:00 x=1 y=2
1:01 B用户 Select x,y –> x=1 y=2
1:02 A用户 Update x=2; Commit;
1:03 B用户 Select x+y –> x+y=4
首先这个结果从单条SQL的角度看,是没有问题的。但是,如果把B的两次查询看作一个整体(事务),那么合理的情况应该是
x+y仍然等于3
或者B再进行一次事务,得出 x=2 y=2 x+y=4 的结果
例子2:更新丢失
1:00 x=1
1:01 B用户 Select x –> x=1
1:02 A用户 Select x –> x=1
1:03 A用户 Update x=2; Commit;
1:04 B用户 Update x=3; Commit;
同样,从单条SQL来讲,没有任何问题。
但是从逻辑的合理性讲,一般的更新操作都是先查再改,换言之
A真正想做的是Update x from 1 to 2
B真正想做的是Update x from 1 to 3
但最终却造成了在B不知情的情况下,把B的初衷改为了Update x from 2 to 3
3.幻影读-Phantom Read
例子:读到了未来
1:00 X1=1 X2=2
1:01 B用户 Select Xi –> X1=1 X2=2
1:02 A用户 Insert X3=3; Commit;
1:03 B用户 Select sum(Xi) –> re=6
其实道理和之前的不可重复读相同,只不过是由Insert引起的罢了。
(甚至Delete也会引起类似的问题,但好像学术界并没有对Delete进行讨论)
Isolation Level
Read Uncommitted:1,2,3都会发生
Oracle中严格禁止脏读
在SQL Server 7.0中,是可以选择该级别的
Read Committed:发生2,3(Oracle的默认级别)
Repeatable Read:发生3
Serializable:都不发生
Oracle的实现方式
Read Committed:默认就实现
Repeatable Read:
1. 悲观锁(select … for update),影响并发
2. 乐观锁(update where 所有字段都作为条件),不影响并发
Serializable:
alter session set isolation_level=serializable/read only
String Reader InputStream Leave a comment
InputStream String2InputStream(String str){
ByteArrayInputStream stream = new ByteArrayInputStream(str.getBytes());
return stream;
}
InputStream –> String
String inputStream2String(InputStream is){
BufferedReader in = new BufferedReader(new InputStreamReader(is));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = in.readLine()) != null){
buffer.append(line);
}
return buffer.toString();
}
String temp = null;
StringBuffer sb = new StringBuffer();
while((temp=br.readLine())!=null){
sb.append(temp+"\n");
}
String string = sb.toString();
JSON解析新办法:JSEL Leave a comment
JSON解析和系列化功能,JSON官方网站是提供了官方的Java实现的(org.json)。
但是,官方的实现并不理想。
不仅接口复杂的要命。而且效率也不理想,基本可以判定是一个不合格产品。
另外一个我比较喜欢的解析器,叫做StringTree。
这个解析器最大的特点就是简单,JSONReader负责解析,JSONWriter负责序列化,成员方法也简单明了。
他的输出格式除原始类型外,返回的Map,和List,充分利用了Java 集合框架。比起官方实现来,干净了很多。
StringTree不仅简单易用,而且性能也非常不错。
根据我的测试结果,StringTree最好。大概是JSEL的两倍。
而官方版本最差,大概只有是JSEL一半。等比数列了,JSEL居中。
不过StringTree有一个bug,如果代码里面有注释,经常出现死循环。
再说JSEL:
与专门的JSON解析相比,JSEL不仅可以解析标准的JSON数据,他还可以处理(忽略)JSON中插入的注释。甚至能处理一些简单的表达式计算,接口简单,性能也还不错。
如:
- {
- "time":24*60*60*1000*365*(2008–1981),
- "data":[1,2,3,4,5],
- key:123
- }
关于JSEL的更多的信息可参看如下连接:
详细介绍:http://code.google.com/p/lite/wiki/JSEL
详细介绍:http://code.google.com/p/lite/downloads/list
使用JSON的方法 Leave a comment
JSON 即 JavaScript Object Natation,它是一种轻量级的数据交换格式,非常适合于服务器与 JavaScript 的交互。本文将快速讲解 JSON 格式,并通过代码示例演示如何分别在客户端和服务器端进行 JSON 格式数据的处理。
Json必需的包
commons-httpclient-3.1.jar
commons-lang-2.4.jar
commons-logging-1.1.1.jar
json-lib-2.2.3-jdk13.jar
ezmorph-1.0.6.jar
commons-collections-3.2.1.jar
以上包可以从
http://commons.apache.org/index.html
http://json-lib.sourceforge.net/
http://ezmorph.sourceforge.net/
中下载到。
出现java.lang.NoClassDefFoundError: net/sf/ezmorph/Morpher错误是因为没有导入ezmorph.jar文件或版本不对。
出现java.lang.NoClassDefFoundError: org/apache/commons/collections/map/ListOrderedMap错误是因为没有导入commons-collections.jar文件或版本不对。
Java代码转换成json代码
1. List集合转换成json代码
List list = new ArrayList(); list.add( "first" ); list.add( "second" ); JSONArray jsonArray2 = JSONArray.fromObject( list ); |
2. Map集合转换成json代码
Map map = new HashMap(); map.put("name", "json"); map.put("bool", Boolean.TRUE); map.put("int", new Integer(1)); map.put("arr", new String[] { "a", "b" }); map.put("func", "function(i){ return this.arr[i]; }"); JSONObject json = JSONObject.fromObject(map); |
3. Bean转换成json代码
JSONObject jsonObject = JSONObject.fromObject(new JsonBean()); |
4. 数组转换成json代码
boolean[] boolArray = new boolean[] { true, false, true }; JSONArray jsonArray1 = JSONArray.fromObject(boolArray); |
5. 一般数据转换成json代码
JSONArray jsonArray3 = JSONArray.fromObject("[‘json’,’is’,’easy’]" ); |
6. beans转换成json代码
List list = new ArrayList(); JsonBean2 jb1 = new JsonBean2(); jb1.setCol(1); jb1.setRow(1); jb1.setValue("xx"); JsonBean2 jb2 = new JsonBean2(); jb2.setCol(2); jb2.setRow(2); jb2.setValue(""); list.add(jb1); list.add(jb2); JSONArray ja = JSONArray.fromObject(list); |
关于 JSON 的Java库, Nutz 的 Json 解析器就非常好:
Java转换成Json:
- Map<String,Object> map = new HashMap<String, Object>();
- map.put("name", "Peter");
- map.put("age", 21);
- map.put("friends", null);
- String result = Json.toJson(map);
这个时候会输出成:
- {
- age :21,
- name :"Peter"
- }
会自动忽略掉值为空的字段.
Json转换成Java:
- String json = "{age :21,name :\"Peter\"}";
- map = Json.fromJson(HashMap.class, Lang.inr(json));
可以支持行注释,块注释, Json.fromJson(Class<T> type, Reader) 可以直接生成一个 Java 对象
详细的可以参见它的一个简单的手册:
http://code.google.com/p/nutz/downloads/detail?name=Nutz_Json_Book.pdf
集群基础概念(转) Leave a comment
构造Cluster是架构师们实现Scalability与High Availability 的最直接用药。所以大家很多都会无意中使用Cluster的思想去设计自己的服务器。其实Java EE里的Clustering已经做得很熟很烂,大家如果烂熟各家vendor对Web,EJB,JNDI,JMS,WebService….的 Cluster实现,再思考自己的烂摊子时,思路便快捷清晰,少很多与同僚们的无谓争论。
JavaEE Cluster的经典范文是Sun的王昱写于2005年的Uncover the hood of J2EE Clustering Preface,更可贵的是dev2dev上的JadeYuan兄弟将它高质的翻成了中文。
一、所谓集群
目的就是以负载均衡(Load Balance)与失败转移(Failover) 实现可扩展性(Scalability)和高可靠性(High Availability),主要实现的功能:
1. Load Balance
算法主要有轮循、权重(根据服务器硬件配置的不同)和随机三种,但更酷的做法是基于负载(直接查探或者服务器主动报告它们的负载)。
2: 2. Health Check
心跳系统与发现协议。Server一般会主动定期多播报告自己状态,也会Ping对方来问候平安。比如Weblogic每10秒会发送一次心跳,如果有30秒没有收到对方服务器的心跳了(考虑到多播可能会丢失数据包)就视对方为阵亡。
3. Session Replication
因为会服务器记录与特定用户的会话信息,Balancer应该把同一用户的请求定位到同一台服务器上。如果该服务器失效,把该用户和会话信息转移到新服务器上时。
如果只要单纯的load balance,不要fail over的话,使用纯硬件如F5已经足够,不需要在软件上做任何事情。
除了Scalability 与High Availability,一个集群还应该对已有代码的最小影响,对性能影响最小,配置与部署简单,以及运行时可监控。
二、Web层群集
Balancer无非Apache/IIS插件,balance Servlet,硬件四层交换机三类,而讨论的重点在Session 信息的Replication 实现上,简单的分有全部服务器冗余备份,三三两两互为冗余备份,中央备份服务器三种模式。
1.多服务器全冗余备份
Tomcat的最为粗糙,最没有扩展性的做法,不提。Sun的怪怪的replacate的内存数据库法HADB可能也属于这种范畴。
2.三三两两互为冗余备份
Weblogic, Jboss and WebSphere 的做法,好主流。A会有B的数据,B会有C的数据,C会有B的数据,如果A出错,就会由C接替A的工作。这种做法的弊端是:
1.要控制failover到备份服务器,Balancer的实现复杂度高。
2. 如果A出错,C就要瞬时承载A、C的操作,很可能将它压垮,针对这点,Weblogic的做法是针对每个session而不是每个Server选择备份服务器,把主备服务器A、B的名字写在用户Cookie里,如果A失效后,Balancer将用户转到服务器C,C会根据用户cookie记录,从B那里获取会话信息。
3.相对没有cluster的方案,需要花额外的时间和内存。
文中没讲的Geronimo使用的WADI,应该也属于这种类型,不过更为灵活,详见Geronimo 叛逆者: 加入集群功能第1部分 和 第2部分。
3. 中央备份服务器
N+ 1模式,一个中央Server存放所有的Session,如果一台Server死了,接管的Server就从中央服务器restore相关数据。可以用数据库(很多应用服务器都支持的最简单,但最慢的模式),也可以采用内存。这种方式好处是cluster服务器上不需要冗余内存,可以failover到任意服务器,cluster服务器全死了中央服务器都不死。坏处就是如果中央服务器死了…如果中央服务器的内存不够了…..另外,多了个 restore的步骤。
使用内存备份session时,Tomcat/JBoss使用的JavaGroups 是一个很好的工具,它的“ Group membership protocols” and “message multicast”特性都非常有用。
另外,无论使用内存还是数据库,都需要串行化Java对象,性能损耗厉害,所以JRun 就采用了Jini架构 ,而Tangosol Coherenc ,Terracotta这些Data Grid方案都提出了自己的session备份做法,整天显示着比传统方案快多少多少。Data Grid分布式缓存本身就是很Enterprise的功能,下篇blog再详述。
三、EJB集群
从stub 调用实际EJB对象时,有三种方法实现负载均衡和fail over:
1. Smart Stub.在stub内维护有效列表,实现负载均衡逻辑,进行实效检测,BEA Weblogic and JBoss 采用。
2. IIOP Runtime Library ,Sun的JES 算法,把算法从客户端的stub移到客户端的IIOP Runtime
3. Interceptor Proxy,IBM做法,把算法移到了服务端,Location Service Daemon (LSD)。
在JNDI查找,EJBHome Stub查找生成EJB实例,调用EJB方法三种时候都可以实现负载均衡,对statefull,stateless,entity bean,又有不同的做法。
四、其他集群
JMS 集群,可以有多个broker组成集群(JBoss,如果要持久化Message,就要把原来嵌入式的数据库改为共享模式),activeMQ还支持多个消费者组成集群,但每个消费者负责同一类的任务,比如订单队列的处理,Server A只处理图书类的订单,或只处理《Programming Ruby 2nd》的订单。
数据库集群有Oracle的RAC,但JDBC本身的failover能力很低,一旦connection 中断,resultset等对象都会失效,Weblogic的连接池会尝试重连。
五、Cluster的神话
1.Failover可彻底避免错误
JBoss的文档用了整整一章来警告你,真的需要http session复制吗?没有http session可以使效率提高很多,而有了的话,并不能避免所有错误。失败转移只能在两次调用间产生作用,在调用时产生的错误是无法恢复的,除非这是个幂等操作(如单纯的get(),而不是put(),无论如何重复操作结果都是一样的),否则,如果A上承载100用户,失败时有20个用户正在进行处理,则只有80个用户能逃出生天平安转移到B。
2.小心编写可集群的程序
1.http session要放能serilaze的对象,对象不要太大,变更时要显式的setAttribute().
2. 注意Cache的使用。如果每个JVM独立使用Cache,会否不一致,如果进行同步,注意开销。
3.不能使用静态变量,如在线用户数,要搞成分布式的 Cache。
4.外部资源如文件系统(一台机器上没有另外一台机器的文件),存成DB或者使用SAN
5.特别服务:如timer服务,基于事件的服务,
P3P解决cookie存取的跨域问题 Leave a comment
在返回给浏览器的 HTTP 头增加 P3P 的信息,代码如下:
response.setHeader("P3P","CP=\"NON DSP COR CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa CONa HISa TELa OTPa OUR UNRa IND UNI COM NAV INT DEM CNT PRE LOC\"");