首先声明,我对asp.net不是很熟,只是偶尔看看。不过java很熟,两者在底层上很象,你的问题也遇到过。1。出现奇怪错误可能是你没有及时释放资源。或是资源已经紧张。可以试试connection pool这样的东西,缓解这个问题;2。jsp里也有这个问题。需要进行代码转换。不过.net好像封装了这部分,可以直接用。3。不加static的话,应该是线程安全的。不过,有些特殊对象还是要小心。java里的servlet就不是线程安全的,除非特别声明(实现特定接口),所有的用户访问的是一个,这是container回调的问题。4。global?只要了解他的工作原理,就没问题。5。更有理由相信1了。6。这个取决于你的软硬件环境。你可以用工具测一下并发访问。比如ms的webstress。
我看错误日志中自从我改成debug模式应用程序重起了后基本上就没有记录错误。
CPU一直不是很高,用户很少,负荷很小。
我用ACT测试,200连接和2000连接。中间出了很多NullReferenceException错误。
性能监视器中加入了.net clr data的current connection pool等计数器,很奇怪这些一直都是0. SQL Server的active transcation一直是4,偶尔变过5.我觉得可能还是编程方面注意的不够,不过这方面很少有文档,msdn上很不全。 只是调试的时候用户少,错误根本看不出来。 工具测试也不能引起实际的错误。 这就比较麻烦。中间很多是.net的本身语言和对象使用问题。
我再接着做测试。
其实,这些问题不仅仅是编程 方面的问题,还有可能是系统集成的问题。
要是访问量太大,最好试一下负载均蘅
加了计数器(以前的加错了) Current Connection Pool: 156
Pooled Connection: 415
Failed Commands: 2047
Total Failed Connections: 318
自己写pool:不会最集中的问题,也是昨晚一直试验的问题:
我在Global中用static 了一个DataSet,里面有一个DataTable。还有一个静态的类的实例,这个类提供一些错误处理和更新函数。 平常不更新,只在一个按钮按了后更新个别字段。 一个最多的错误就是在更新这个字段的时候出现NullReferenceException. 我是用 DataTable.Find找到DataRow,然后用字符串索引给这个字段赋值。 试了很多方法,Application.Lock, lock( theDataTable) 都不管用。
正常的时候不出错,我一用ACT发起测试,甭管测试结束与否,再去更新就发出异常。 另外一个比较多的错误是IndexOutOfRengeException. 但发生这个错误的时候的操作是只读的而且不是全程的。 有时出有时不出。
还有SqlException的几种错误。 错误很少出,所以比较郁闷
DataTable msdn解释是读的时候是线程安全的,写的时候需要同步。
我不知道这个同步除了lock还能怎么做。不过现在的问题类型越来越多,居然还出过 如果Page没有允许Session则Session不能用 这样的笑话错误。
没有遇到可视的和记录的错误,只是监视器中Exception thrown比较大,有3000多,不知道是哪里的。 我现在想可能是和前几天ASP程序有关。开始有个ASP程序有毛病,访问的时候会内存突涨,到顶了inetinfo就被关掉重起了。页面也是频繁无法打开。后来把那个asp给停了,www service 和SQL Server都重起过,但机器没有重起。 asp.net出现的问题可能是遗留的。 只有接着做测试了。不知各位有没有自己编过ACT的脚本?我用录制生成的脚本,这样每次操作都是一样的。 懒得写脚本。 如果是资源用尽主要是什么资源?内存用的不大,Connection有可能,但是出的错误似乎和Connection没有关系。 其它还有什么主要的资源呢?
ACT也很不稳定, 一台机子(就是开始说的那个服务器)现在是无法启动测试,说提供程序加载失败
另外一台(后面那个服务器)能测,但是查看结果的时候就出错关闭。
看来微软推广.net太仓促了。
每个connection pool默认能用100个connection。
应用程序重新启动后创建连接的时候会自动增加一个connection pool
自动的pool管理不会强行回收未关闭的connection
大约弄了20多个P,1000左右的C。
后来一用act连我的测试页 机子就被搞死了。屡试不爽。而我测试页中是按了按钮才开始费connection的,起始只是一个简单的绑定.. 自己写pool管理不知道怎么写。
pool管理应该是.net framework做的,我将IIS重起对已经存在的pool根本没有影响。不知道.netframework是通过什么运行的,找不到相应的服务。所以不好控制。 pool管理要是做在应用程序中的话估计接管不了.net的库。再去完成一套connection太麻烦了。 SqlConnection的connect string 中可以包括如Max Pool Size, Connection Lifetime这样的控制,我昨天试了一下,Max Pool Size管用,我设成1000也没出什么问题,Connection Lifetime基本上不管用,只有一次运行的时候能到点结束未关闭的Connection,后来就根本不起作用了。 现在错误日志中倒是没有记录什么错误。 再过一个多星期会大负荷运行几天,同一时间会有2000多个用户不停的连接。而且要保证不出问题。 之后我会结贴,同时证明一下asp.net是否能在大量高负荷的访问下稳定地运行。
import java.util.*;
import misc.Const;/** A class for preallocating, recycling, and managing JDBC connections.
* <P>
* Taken from Core Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.coreservlets.com/.
* © 2000 Marty Hall; may be freely used or adapted.
*
* Modified by Miles Zhang 07/2001 at Oohla, Inc.
*/public class ConnectionPool implements Runnable { // instead of util.ArrayList, we use Vector for it is synchronized.
private Vector availableConnections, busyConnections;
private boolean connectionPending = false;
private StringBuffer sbDebug = new StringBuffer(); private int freeTimes = 0;
private final static int REUSE_UP_LIMIT = 500; // instance to be return;
private static ConnectionPool pool = null; public static synchronized ConnectionPool getInstance()
throws SQLException {
if(pool==null) pool = new ConnectionPool();
return pool;
} private ConnectionPool()
throws SQLException { if (Const.initialConnections > Const.maxConnections) throw new SQLException("initialConnections should no more than maxConnections in dbhandler.Const");
availableConnections = new Vector(Const.initialConnections);
busyConnections = new Vector();
for (int i=0; i<Const.initialConnections; i++) {
availableConnections.addElement(makeNewConnection());
}
} public synchronized Connection getActiveConnection()
throws SQLException {
sbDebug = new StringBuffer(); if (!availableConnections.isEmpty()) {
Connection existingConnection = (Connection)availableConnections.lastElement();
int lastIndex = availableConnections.size()-1;
availableConnections.removeElementAt(lastIndex);
// If connection on available list is closed (e.g.,it timed out), then
// remove it from available list and repeat the process of obtaining a
// connection.Also wake up threads that were waiting for a connection
// because maxConnection limit was reached.
if (existingConnection.isClosed()) {
Debug("Exist connection closed, notify all and get a new free connection.");
notifyAll(); // Freed up a spot for anybody waiting
return(getActiveConnection());
} else {
busyConnections.addElement(existingConnection); // debug when connection get.
sbDebug.append("Get connection successfully!");
sbDebug.append("\n Total connection: ");
sbDebug.append(this.totalConnections());
sbDebug.append("\n Available connections: ");
sbDebug.append(availableConnections.size());
sbDebug.append("\n Busy connections: ");
sbDebug.append(busyConnections.size());
sbDebug.append("\n ======================");
Debug(sbDebug.toString()); return(existingConnection);
}
} else { // Three possible cases:
// 1) You haven't reached maxConnections limit. So
// establish one in the background if there isn't
// already one pending, then wait for
// the next available connection (whether or not
// it was the newly established one).
// 2) You reached maxConnections limit and waitIfBusy
// flag is false. Throw SQLException in such a case.
// 3) You reached maxConnections limit and waitIfBusy
// flag is true. Then do the same thing as in second
// part of step 1: wait for next available connection.
if ((totalConnections()<Const.maxConnections) && !connectionPending) {
makeBackgroundConnection();
} else if (!Const.waitIfBusy) {
throw new SQLException("Connection limit reached");
}
// Wait for either a new connection to be established
// (if you called makeBackgroundConnection) or for
// an existing connection to be freed up.
try {
this.Debug("No Availible Connection, Waiting...");
wait();
} catch(InterruptedException ie) {}
// Someone freed up a connection, so try again.
return(getActiveConnection());
}
} // You can't just make a new connection in the foreground
// when none are available, since this can take several
// seconds with a slow network connection. Instead,
// start a thread that establishes a new connection,
// then wait. You get woken up either when the new connection
// is established or if someone finishes with an existing
// connection. private void makeBackgroundConnection() {
connectionPending = true;
try {
Thread connectThread = new Thread(this);
connectThread.start();
} catch(OutOfMemoryError oome) {
// Give up on new connection
}
}
try {
Connection connection = makeNewConnection();
synchronized(this) {
availableConnections.addElement(connection);
connectionPending = false;
notifyAll();
}
} catch(Exception e) { // SQLException or OutOfMemory
// Give up on new connection and wait for existing one
// to free up.
}
} // This explicitly makes a new connection. Called in
// the foreground when initializing the ConnectionPool,
// and called in the background when running. private Connection makeNewConnection()
throws SQLException {
try {
// Load database driver if not already loaded
Class.forName(Const.DriverName);
// Establish network connection to database
Connection connection = DriverManager.getConnection(Const.JdbcURL, Const.Username, Const.Password);
Debug("Make new connection successfully!");
return(connection);
} catch(ClassNotFoundException cnfe) {
// Simplify try/catch blocks of people using this by
// throwing only one exception type.
throw new SQLException("Can't find class for driver: " + Const.DriverName);
}
} public synchronized void freeConnection(Connection connection) {
sbDebug = new StringBuffer();
busyConnections.removeElement(connection);
availableConnections.insertElementAt(connection,0);
// Wake up threads that are waiting for a connection
sbDebug.append("Free connection successfully, notifying...");
sbDebug.append("\n Total connection: ");
sbDebug.append(this.totalConnections());
sbDebug.append("\n Available connections: ");
sbDebug.append(availableConnections.size());
sbDebug.append("\n Busy connections: ");
sbDebug.append(busyConnections.size()); // reach up limit.release.
if(++freeTimes>REUSE_UP_LIMIT) {
freeTimes = 0;
sbDebug.append("\n================ Time to close some fatigued connections ===============");
try {
connection.close();
} catch (SQLException ex) {}
} // Available Connection
sbDebug.append("\n ======================");
sbDebug.append("\n ========== " + (REUSE_UP_LIMIT - freeTimes) + " ========");
sbDebug.append("\n ======================");
Debug(sbDebug.toString());
notifyAll();
} public synchronized int totalConnections() {
int intTotal = availableConnections.size() + busyConnections.size();
return intTotal;
} /** Close all the connections. Use with caution:
* be sure no connections are in use before
* calling. Note that you are not <I>required</I> to
* call this when done with a ConnectionPool, since
* connections are guaranteed to be closed when
* garbage collected. But this method gives more control
* regarding when the connections are closed.
*/ public synchronized void closeAllConnections() {
this.closeConnections(availableConnections);
availableConnections = new Vector();
this.closeConnections(busyConnections);
busyConnections = new Vector();
} private void closeConnections(Vector connections) {
try {
for (int i=0; i<connections.size(); i++) {
Connection connection = (Connection)connections.elementAt(i);
if ( !connection.isClosed() ) {
Debug("Close connection");
connection.close();
}
}
} catch(SQLException sqle) {
// Ignore errors; garbage collect anyhow
}
} public synchronized String toString() {
String info =
"ConnectionPool(" + Const.JdbcURL + "," + Const.Username + ")" +
", available=" + availableConnections.size() +
", busy=" + busyConnections.size() +
", max=" + Const.maxConnections;
return(info);
} public String info() {
Connection tempConn;
// all available connections info.
for (int iCnt = 0; iCnt < availableConnections.size(); iCnt++) {
tempConn = (Connection)availableConnections.elementAt(iCnt);
sbDebug.append("\n Available Connection #");
sbDebug.append(iCnt);
sbDebug.append(": ");
try {
sbDebug.append(tempConn.isClosed());
} catch (SQLException e) {
sbDebug.append(e.toString());
}
}
// Busy Connection
for (int iCnt = 0; iCnt < busyConnections.size(); iCnt++) {
tempConn = (Connection)busyConnections.elementAt(iCnt);
sbDebug.append("\n Busy Connection #");
sbDebug.append(iCnt);
sbDebug.append(": ");
try {
sbDebug.append(tempConn.isClosed());
} catch (SQLException e) {
sbDebug.append(e.toString());
}
} return sbDebug.toString();
} private void Debug(String DebugInfo) {
util.Debug.Debug("dbhandler.ConnectionPool",DebugInfo);
}
}
基本独立,只是有一个util.Debug类,写日志的,不用管,去掉就行了。每隔一段时间,就关闭一个connection,是随机的,反正是能达到目的。在启动web server的时候,就
pool = dbhandler.ConnectionPool.getInstance();
停止时:
pool.closeAllConnections();用的时候,就把它当作在库和bean之间的一层,
所有的conn.open的地方,都改成
pool = ConnectionPool.getInstance();
conn = pool.getActiveConnection();conn.close的地方,都改成
pool.freeConnection(conn);pool是一个singleton类,只有一个instance,不能构造。祝你好运。 :)
可行的办法也许是自己做个全程静态的 封装 connection的collection或者arraylist的类,其他地方都从这获取Connection。 但是我往arraylist中加入connection的时候总是说无法加入。后来就没有再试。
List list = Collections.synchronizedList(new ArrayList(...));
这样还不如用vector。这样实现的不是假pool,而是真pool。pool本身就是底层无关的,不过这样的话,确实无法关闭,只能返回到系统pool。可以试着把系统pool关掉。这一点.net做的是ms一贯风格,不设置,就用。java默认是没有pool的(至少1.3是这样)。人人对pool的需求都不一样。他也有一个pooledconnection,但是大多情况下,都是自己写,这样灵活些。理论上讲pool本来就在应用程序级,自己写一个不见的比系统自带的效率低,而且系统的pool要预留更多接口,很可能是个重量级的家伙。此外,sql server本身也值得怀疑。一前我做过一个网站,用jdbc连sqlserver,有一个地方库操作非常频繁(但不是并发),查查出现sqlserver死掉的状况,换成oracle就没问题。.net对sqlserver做了很多优化,但是它的性能本身不知道怎么样。到处都是广告,我们也无法知道真实状况。
相同的东西,在本机浏览器中浏览, 和在别的机器上浏览,结果是不一样的。
相同的程序,在2000下IIS5中运行和.net server的IIS6下运行结果是不一样的。 通常是.net server的更稳定一些, 但是.net framework的版本是比2k下的要低。感觉很多时候出null reference都是GC提前把对象回收了。即使还在生存期内。 有时候Response.Write("aaa")都说Null Reference!!!真是令人头大啊!
超时时间已到。在操作完成之前超时时间已过或服务器未响应。System.Data.SqlClient.SqlException
常规网络错误。请检查您的网络文档。System.Data.SqlClient.SqlException
未将对象引用设置到对象的实例。System.NullReferenceException
将截断字符串或二进制数据。System.Data.SqlClient.SqlException
索引超出范围。必须为非负值并小于集合大小。System.ArgumentOutOfRangeException
要求非负数字。System.ArgumentOutOfRangeException
当前命令发生了严重错误。应放弃任何可能产生的结果。System.Data.SqlClient.SqlException
开始时间 System.IndexOutOfRangeExceptionsource 一般是.Net SqlClient Data Provider,System.Data,xknew(我的程序名)
不过我感觉,MS的Pool管理不是很好。
错误中xknew引发的错误比例很小。 大部分都是system.data和.net SqlClient发出的。
整个出错的概率估计在 千分之1-5之间 (100多用户的时候)
用户数多的话估计比例就大了。有时候还会出现aspnet_wp进程不响应。 虽然配置中可以设多长时间不响应的话自动强行重起进程,我也设的比较短, 但这样总是很讨厌。一天出好多次。我另外编的一个多线程的程序,单用户访问就出现很多aspnet_wp意外停止的错误。程序不长,但外部毛病多种多样,不正常的现象随机出现。 而在.net server中就基本上没有这类错误。
Connection Pool:
加了计数器(以前的加错了) Current Connection Pool: 156
Pooled Connection: 415
Failed Commands: 2047
Total Failed Connections: 318这个计数器在哪儿加?
这个计数器在.NET CLR Data里面。 实例是每次启动的应用程序一个,每次重起应用程序就会多一个。 所以一般加_global_就可以了。