首先声明,我对asp.net不是很熟,只是偶尔看看。不过java很熟,两者在底层上很象,你的问题也遇到过。1。出现奇怪错误可能是你没有及时释放资源。或是资源已经紧张。可以试试connection pool这样的东西,缓解这个问题;2。jsp里也有这个问题。需要进行代码转换。不过.net好像封装了这部分,可以直接用。3。不加static的话,应该是线程安全的。不过,有些特殊对象还是要小心。java里的servlet就不是线程安全的,除非特别声明(实现特定接口),所有的用户访问的是一个,这是container回调的问题。4。global?只要了解他的工作原理,就没问题。5。更有理由相信1了。6。这个取决于你的软硬件环境。你可以用工具测一下并发访问。比如ms的webstress。

解决方案 »

  1.   

    谢谢分析  我编程的时候还是比较注意资源的。除了有一个类中不同的函数间公用一个connection没有每次打开关闭,其他的都是及时关闭的。 看了帮助,ADO.NET的SQLServer连接池管理是自动的。    关于Global连msdn里都难查出帮助。 我只是将固定功能的类的实例放在了Global中这样好引用。    晚点等没人了测试,用ACT。 不过觉得测试结果不能表明所有的问题。    另外服务器是专用服务器,Xeon + 2G mem,虽然和web 和sql server放在一起,应该也影响不大。
      

  2.   

    可以看看程序出错的时候,哪些进程占用了最多的内存cpu什么的。当然了,用工具模拟一下还是很必要。另外,不知道默认情况的内建连接池效果如何。很多情况下,如果不设置,效果反而不好,还是要有足够的注意。查查文档。2000平台下的Xeon + 2G mem只能是中档配置,不算很出色。不能排除性能问题。另外,我觉得.net不稳定的可能性很小。他已经经过了beta测试,这方面应该不存在问题,要不然,早就有人叫了。有什么进展,咱们再交流。
      

  3.   

    后来用户一直比较少,就没有出多少错误。
    我看错误日志中自从我改成debug模式应用程序重起了后基本上就没有记录错误。
    CPU一直不是很高,用户很少,负荷很小。
    我用ACT测试,200连接和2000连接。中间出了很多NullReferenceException错误。
    性能监视器中加入了.net clr data的current connection pool等计数器,很奇怪这些一直都是0. SQL Server的active transcation一直是4,偶尔变过5.我觉得可能还是编程方面注意的不够,不过这方面很少有文档,msdn上很不全。 只是调试的时候用户少,错误根本看不出来。 工具测试也不能引起实际的错误。 这就比较麻烦。中间很多是.net的本身语言和对象使用问题。
    我再接着做测试。
      

  4.   

    我在使用的时候曾发生过访问量大时session经常丢失的问题,后将web.config文件的sessionState的mode属性由“InProc”改为“StateServer”并在“服务”中启动asp.netstateservice后再也没出过问题,但你说的问题还未碰到过。
      

  5.   

    首选 ,。NET本身的稳定性是不错的,至少比ASP强多了。想想看,MS的网站都是用ASP做的,访问量那么大,都没有什么问题。
    其实,这些问题不仅仅是编程 方面的问题,还有可能是系统集成的问题。
    要是访问量太大,最好试一下负载均蘅
      

  6.   

    实在不行自己写个pool,看看有没有什么不同。等你的好消息!
      

  7.   

    .NET本身的稳定性很好,但是对服务器的配置要求稍显苛刻,资源问题严重,所以及时释放资源是很有必要的,不能依赖它本身的资源回收机制。
      

  8.   

    1、编程中注意cache的使用2、注意connection的关闭。推荐写公用数据库处理类(参考petshop)3、asp.net的dll发布以后,可以采用ngen预编译,提升速度。
      

  9.   

    我也碰到过这样的问题,系统访问一高就报错了,编译成DLL是一个好主意,但是好多人并不是使用VS.NET开发的,这样的编译就比较麻烦!!
      

  10.   

    Connection Pool:
       加了计数器(以前的加错了)  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的几种错误。    错误很少出,所以比较郁闷
      

  11.   

    四百多个?这不等于一直在new么?并发访问有多少,需要这么大的pool?静态的dataset,在启动时实例化,然后访问的时候lock?dataset是离线的么?好像一个connection打开时间太长,会有问题。唉,怎么说都是隔靴搔痒
      

  12.   

    我不知道pooled connection是在池中的空闲连接还是池中分配出去的连接。DataSet是离线的, 在application start的时候填充,以后基本上都是读。
    DataTable msdn解释是读的时候是线程安全的,写的时候需要同步。
    我不知道这个同步除了lock还能怎么做。不过现在的问题类型越来越多,居然还出过 如果Page没有允许Session则Session不能用 这样的笑话错误。
      

  13.   

    重起了一下服务器,运行了半个小时,现在Sessions 40,connection pools 1-2, pooled connections 5, Exceptions thrown 478,变化比较大的是%Time of GC。不知这个具体是什么意思,但不是GC占的CPU。    以前的错误还没有出。  以前出错的时候也是一阵一阵的,这个地方出毛病的话连着出。应用程序重新启动后可能就不出了,改其他地方了。 我测试的时候出异常的基本在几个地方,实际用户多引起的异常总是在其他几个地方。
      

  14.   

    pooled connection空闲还是分配应该无所谓。connection 5个?访问量小了么?session也只有40。Time of GC应该是GC间隔时间或是进行时间吧。MSDN没说GC需要干涉。应该也无关。而且它本身也就应该是这样。错误不确定,应该是稳定性问题如果怀疑是程序问题,可以做个简单的东西,测一下,看看究竟怎么回事儿。
      

  15.   

    重起后的访问量一直很小,现在Current Pooled Connections=1~2 
      没有遇到可视的和记录的错误,只是监视器中Exception thrown比较大,有3000多,不知道是哪里的。   我现在想可能是和前几天ASP程序有关。开始有个ASP程序有毛病,访问的时候会内存突涨,到顶了inetinfo就被关掉重起了。页面也是频繁无法打开。后来把那个asp给停了,www service 和SQL Server都重起过,但机器没有重起。 asp.net出现的问题可能是遗留的。   只有接着做测试了。不知各位有没有自己编过ACT的脚本?我用录制生成的脚本,这样每次操作都是一样的。 懒得写脚本。   如果是资源用尽主要是什么资源?内存用的不大,Connection有可能,但是出的错误似乎和Connection没有关系。 其它还有什么主要的资源呢?
      

  16.   

    感觉vs.net还是稳定性很差。 我把整个东西原样放在另外一台服务器上,而且重新编译了, 却有了不同的情况。 Exception thrown一直都是0,Errors Total却要大的多,ACT测试的时候dns错误,http错误,套接字错误都很大,而在前一个服务器上测试的时候都是 0。
    ACT也很不稳定, 一台机子(就是开始说的那个服务器)现在是无法启动测试,说提供程序加载失败
    另外一台(后面那个服务器)能测,但是查看结果的时候就出错关闭。
    看来微软推广.net太仓促了。
      

  17.   

    做了个小测试。  循环创建connection然后执行个小查询不关闭连接。
      每个connection pool默认能用100个connection。
      应用程序重新启动后创建连接的时候会自动增加一个connection pool
      自动的pool管理不会强行回收未关闭的connection
      大约弄了20多个P,1000左右的C。
       后来一用act连我的测试页 机子就被搞死了。屡试不爽。而我测试页中是按了按钮才开始费connection的,起始只是一个简单的绑定..  自己写pool管理不知道怎么写。
      

  18.   

    需要注意的就是稀缺资源,象内存和connection。内存本身不一定耗尽,但是heap大小和内存不同。在java里,可以设:java -Xms128m -Xmx128m不知道有没有对应,也不知道有没有用。Errors还算正常,因为测试静态网页,也会有errors。但是按你的描述,好像是又稳定性问题。错误不容易再现。pool本身是不会回收的,你可以自己写一个pool,检查使用时间,超时就强制关闭。我源来看某些文档,说是使用时间过长的connection也会有疲劳。我写过一个pool,是java的,不知道对你有没有帮助,想来应该和C#类似,不过多线程的部分要改改。如果你要,我给你发过去。祝你好运!
      

  19.   

    发过来吧。不知道相通否。 我是不知道要写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是否能在大量高负荷的访问下稳定地运行。
      

  20.   

    package dbhandler;import java.sql.*;
    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/.
     *  &copy; 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
    }
    }
      

  21.   

    public void run() {
    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);
    }
    }
      

  22.   

    太长了,所以分成了两部分贴。直接合就行。
    基本独立,只是有一个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,不能构造。祝你好运。 :)
      

  23.   

    没学过java,不知道运行方式。  dbhandler在.net 中不知道有没有,无法得到的话就没法控制pool了。 就像前面说的一样,pool控制不在IIS中,也不是aspwp账号执行的,因此应用程序中应该无法也没有权限去控制。
       可行的办法也许是自己做个全程静态的 封装 connection的collection或者arraylist的类,其他地方都从这获取Connection。 但是我往arraylist中加入connection的时候总是说无法加入。后来就没有再试。
      

  24.   

    dbhandler是我的包,也就是c#里的namespace。c#里的arraylist是线程安全的么?java里就不是,所以我这里用了效率低的vector。
      

  25.   

    ArrayList有ArrayList.Synchronized方法来进行同步。MSDN:此类型的公共静态(在 Visual Basic 中为 Shared)成员对于多线程操作是安全的。不能保证实例成员是线程安全的。只要集合未被修改,ArrayList 就可安全地同时支持多个读取器。若要保证 ArrayList 的线程安全,则必须通过由 Synchronized 方法返回的包装来执行所有操作。枚举一个集合在本质上不是一个线程安全的过程。甚至在对集合进行同步处理时,其他线程仍可以修改该集合,这会导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。感觉这样自己实现的假pool作用不大,一是因为最后还要通过底层的pool管理,而其可以通过connectionstring来控制pool大小和生存期,所以意义不是很大 二是自己插入的连接管理多做了不少操作,对系统效率有影响。
      

  26.   

    这就和java的arraylist一模一样了。java是
    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做了很多优化,但是它的性能本身不知道怎么样。到处都是广告,我们也无法知道真实状况。
      

  27.   

    最后的用户同时访问量并不大,没有看太出来。  不过还是有错。继续怀疑asp.net当前的稳定性。作了个小程序,和Database没有关系,也经常不定时出null reference 和 invalid cast等等的错误,  而这些根本是不应该出的。
    相同的东西,在本机浏览器中浏览, 和在别的机器上浏览,结果是不一样的。
    相同的程序,在2000下IIS5中运行和.net server的IIS6下运行结果是不一样的。 通常是.net server的更稳定一些, 但是.net framework的版本是比2k下的要低。感觉很多时候出null reference都是GC提前把对象回收了。即使还在生存期内。 有时候Response.Write("aaa")都说Null Reference!!!真是令人头大啊!
      

  28.   

    唉~~~~~果真如此的话,就的确是有些问题了gc的机制应该是查引用的吧,怎么会这样不知道强制gc的话,会不会错误更多。
      

  29.   

    贴一下我出错误日志的记录:
    超时时间已到。在操作完成之前超时时间已过或服务器未响应。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(我的程序名)
      

  30.   

    形形色色,呵呵。这样参考价值也就不大了只有你的这一个程序xknew有问题?因为这个被访问的次数多,还是因为它本来就是较底层的类?
      

  31.   

    占个位置,看看你们最后的结果是什么。
    不过我感觉,MS的Pool管理不是很好。
      

  32.   

    xknew 是 我的应用程序名,也就是namespace名.
    错误中xknew引发的错误比例很小。 大部分都是system.data和.net SqlClient发出的。
    整个出错的概率估计在 千分之1-5之间 (100多用户的时候)
    用户数多的话估计比例就大了。有时候还会出现aspnet_wp进程不响应。 虽然配置中可以设多长时间不响应的话自动强行重起进程,我也设的比较短, 但这样总是很讨厌。一天出好多次。我另外编的一个多线程的程序,单用户访问就出现很多aspnet_wp意外停止的错误。程序不长,但外部毛病多种多样,不正常的现象随机出现。  而在.net server中就基本上没有这类错误。
      

  33.   

    楼上的老兄,引用你的话:
    Connection Pool:
       加了计数器(以前的加错了)  Current Connection Pool: 156
       Pooled Connection: 415
       Failed Commands: 2047
       Total Failed Connections: 318这个计数器在哪儿加?
      

  34.   

    to ruirui
     这个计数器在.NET CLR Data里面。 实例是每次启动的应用程序一个,每次重起应用程序就会多一个。 所以一般加_global_就可以了。