可以扩展的系统架构和验证(持续增加中......)

本文详细阐述了在Web应用中优化用户体验的关键技术,包括动态加载JS、设定JS过期时间、负载均衡配置、缓存数据、数据库连接池监控与调优等措施。通过实施这些策略,能够有效改善系统性能,提升用户体验,特别是在网速较慢的环境下。文中还介绍了如何通过调整并发数、使用JMX监控c3p0连接池、设置JVM参数等手段进一步优化系统性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2012年12月21日, 今天是世界末日, 外面阳光超好, 好久没有见到这样的天气了。

系统架构

1、客户层

      js, css, data的结合

      js数据模版

     html模版


      js加载效率的提升, 因为在无法避免js的数量的增多, 体积的增大的情况下。 如何有效的加载js就显的尤为重要。采用以下方法

      a、动态加载js, 不说了, 直接上代码:

           

Skip.getJSActionByAjax1 = function (rootObject, url) {	
	var myAjax = new Ajax.Request(url, {
		method : "get",		
		asynchronous : false,
		onSuccess : function(request) {
			var text = request.responseText;
			//alert(text);
			//Skip.includeJsText(rootObject,text);
			Skip.includeJsSrc( rootObject, url); 
			
		},
		onFailure : function(request) {
			alert("出错了!" + "\n" + request + "\n" + request);			
			return result;
		}
	});	
}  

//导入文件
Skip.includeJsSrc =function (rootObject, fileUrl){ 
        if ( rootObject != null ){ 
             var oScript = document.createElement( "script" ); 
             oScript.type = "text/javascript"; 
             oScript.src = fileUrl; 
             rootObject.appendChild(oScript); 
       } 
      }

【注明:这里用到了prototype.js】

var myjslist= new Array();
//执行时间的方法
function dyloadjs(fileName, funcJs,showReVal,e,fieldName2,funcName2){
   //alert(fileName);
   var find = true;
   for(var i=0;i<myjslist.length;i++)
   {
       if(myjslist[i]==fileName) {
         find = false;
         break;
       }   
   }
   if(find) {
       myjslist.push(fileName);
       var rootObject=document.getElementById("myjs");
       Skip.getJSActionByAjax1(rootObject,fileName);  
       Skip.getJSActionByAjax1(rootObject,fileName); 
     //Skip.includeJsSrc( rootObject, fileName);
    }  
    //TODO 
    //调用js函数, 运行
}


【注明,以上的方法是要求同步加载的, 因为不能采用回调的方式执行,并且对执行的顺序要求非常的高。而且以前代码量很大, 很难修改。

                这个地方有个问题   getJSActionByAjax1 方法被调用了2次, 

              因为在ie8下面, 如果不调用2次, 就没有效果, 始终方法找不到,ie9下面就没有问题,

              chrome, safiri等高档浏览器, nnd, 加载js就不是阻塞的, 所以, 这种方式无效, 因为没有等js加载完

             就开始执行下面的方法了。  】

        采用了这个办法, 可以有效的控制js的加载的顺序, 有图有真相:

       

        js的加载按照预期的方式进行, 把js的加载的事情分割到了不同的时间和区域, 这样可以有效的改善用户体验

        但是仔细看上图, 可以清楚的发现js每个都加载了3次, 是因为上面的方法被调用了2次ajax请求, ajax成功以后还有一次是include js src的请求, 

        但是为啥不是4次, 这个地方有写疑问,难道ie自己发现已经script头有了, 就不在重复加载了?

        这个地方滋生了2个问题

           1、如何有效的js的请求次数从3次变成1次呢, 好像这个不太可能。最主要直接create script dom节点的话, 不能阻塞加载的线程, 所以这个

                 问题暂时放一下 

           2、换个思路, 能不能减少ajax的请求时间呢?  答案是完全可以的。如果这样的话, 后面的时间都是0的话,完全可以弥补次数多的遗憾, 还有什么

                 比耗费0时间效率更高呢。 

      b、设定js的过期时间expires 属性, (这个应该是见效最明显的办法了)  , tomcat加配置简单易用, 以tomcat7为例:

     

<filter>
 <filter-name>ExpiresFilter</filter-name>
 <filter-class>org.apache.catalina.filters.ExpiresFilter</filter-class>
 <init-param>
    <param-name>ExpiresByType image</param-name>
    <param-value>access plus 10 minutes</param-value>
 </init-param>
 <init-param>
    <param-name>ExpiresByType text/css</param-name>
    <param-value>access plus 10 minutes</param-value>
 </init-param>
 <init-param>
    <param-name>ExpiresByType application/javascript</param-name>
    <param-value>access plus 10 weeks</param-value>
 </init-param>
</filter>
...
<filter-mapping>
 <filter-name>ExpiresFilter</filter-name>
 <url-pattern>/*</url-pattern>
 <dispatcher>REQUEST</dispatcher>
</filter-mapping>

配置后效果图, 很好很强大。



    

呵呵, 后面的js的加载时间都变成了0, 哇塞, 这个很好很强大。 对于一些比较稳定的, 少修改的系统来说, 这个参数配置绝对是超值。


同时注意到这个: 

org.apache.catalina.filters.ExpiresFilter

是不是可以重载这个类, 实现自己具体那些文件需要这种处理。 


总结: 通过以上2步骤的配置, js的加载基本上通过分时、 分阶段的加载的话。可以有效的改善用户体验。 特别对一些网速比较慢的情况。

     

2、 web层

    负载均衡:

    使用nginx做负载均衡

     nginx的配置信息如下及字段说明:

#运行nginx所在的用户名和用户组
#user  nobody;

#启动进程数
worker_processes  2;

#设置全局错误日志存放路径和日志级别
#error_log  logs/error.log  debug;
#error_log  logs/error.log  info;
error_log  logs/error.log  notice;
#error_log  logs/error.log  warn;
#error_log  logs/error.log  error;
#error_log  logs/error.log  crit;

#pid        logs/nginx.pid;

#设定 nginx 进程可以打开的最大文件描述符数量
worker_rlimit_nofile 65535;
#工作模式及连接数上限
events {
	worker_connections  65535;
}

http {
	#设定mime类型
	include       mime.types;
	#设置默认的Content-Type,http head中如果没有设置的情况下才会使用
	default_type  application/octet-stream;

	#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
	#                  '$status $body_bytes_sent "$http_referer" '
	#                  '"$http_user_agent" "$http_x_forwarded_for"';

	#禁止访问日志,启用的话时间长了文件会很大,而且我们暂时不会启用自动脚本清理日志
	#access_log  logs/access.log  main;
	access_log off;

	# 启用sendfile()函数,大部分情况下提高复制文件的速度
	sendfile        on;
	# 配合 sendfile 使用
	tcp_nopush     on;

	#keepalive_timeout  0;
	# 客户端到服务器端的连接持续有效时间
	keepalive_timeout  65;

	# gzip 相关设置
	gzip  on;
	gzip_min_length    1k;
	gzip_buffers       4 16k;
	gzip_http_version  1.0;
	gzip_comp_level    6;
	gzip_types         text/plain text/json text/css text/javascript text/xml application/x-javascript application/xml;
	gzip_vary          on;

	# 设置可以在 proxy_pass 中使用的一组代理服务器
	upstream tomcat{
		# max_fails 是检查服务器失败次数,当失败次数达到设定值时停用该服务,0表示关闭检查
		# server 可以设置多个
		server localhost:8888 max_fails=0;
	}

	# 虚拟主机配置
	server {
		# nginx 监听的端口
		listen       8080;
		# 根据客户端请求 header 中的 host 域名匹配响应的虚拟主机
		server_name  localhost;


		#============静态文件配置============

		# location 匹配 URI,可以使用字符串,也可以使用正则表达.使用正则表达式时必须使用 ~* 或者 ~ 前缀
		# ~* 前缀表示不区分大小写匹配
		# ~ 前缀表示区分大小写匹配
		# ^~ 前缀表示匹配到字符串后不再取检查正则表达式
		# = 前缀表示精确匹配URI 例如"location = /test"只能匹配"/test",而"test123"则不能被匹配
		location ~* /web/(.*)\.(gif|png|jpg|jpeg|bmp|swf)$ {
			# rewrite 重写url,去掉web等tomcat的webapp名称,为下面的root路径做准备
			rewrite ^/web/(.*)$ /$1 break;

			# 指定请求的文档根目录.
			# 例如 location /i/ {root /home/www/;}时,请求的 URI 是 "/aaa/bb.gif",
			# 则会返回 "/home/www/aaa/bb.gif" 的内容.
			# 即 root 指向的目录内的结构必须和 URI 相同,这与下面的 alias 不一样
			# 并且使用相对路径定位的时候,不是nginx.conf文件所在的路径,
			# 而是编译时指定的prefix路径,或者运行是手动指定的路径,alias定位也是一样
			root "../Web/";
			
			# 设置 http head 中 "Expires"和"Cache-Control" 的值,负数时"Cache-Control"的值为no-cache
			# off 表示不修改"Expires"和"Cache-Control"的值
			expires 30d;
		}

		

		#============临时文件路径跳转============

		

		#error_page  404              /404.html;

		# 50X错误所显示的错误页面
		error_page   500 502 503 504  /50x.html;
		location = /50x.html {
			root   html;
		}

		#禁止访问WEB-INF文件夹
		location ~ /(WEB-INF)/{
			deny all;
		}

		# 其他都跳转到tomcat
		location / {
			# 设置代理服务器
			proxy_pass http://tomcat;

			# 设置代理的 http 头中的 host 值为远程连接原本的 host 值
			proxy_set_header HOST $http_host;

			# 设置代理跟后端服务器连接的超时时间.
			# 该时间不是服务器返回页面的事件,而是发起握手等候响应的超时时间.
			# 开发人员调试 java 端程序时可以适当的调长时间,
			# 不然调试时间太长 nginx 会以为后端服务器挂掉而返回500错误.
			# 生产环境中应该设置比较短的时间,不然当后端服务器关闭或者挂掉后 nginx 还在等候,
			# 导致客户端页面在长时间的等待而不是返回错误页面
			proxy_connect_timeout 5s;

			# 关闭对被代理的服务器的应答缓冲
			proxy_buffering off;

			# 客户端允许上传的最大文件大小
			client_max_body_size 500m;

			# 超时必须设置一秒以上,不然IE下载文件会报错
			expires 1s;
		}


		# 这两个路径404时不记录日志
		location = /robots.txt  { access_log off; log_not_found off; }
		location = /favicon.ico { access_log off; log_not_found off; }	
	}
}

     

    web服务优化调优--tomcat,

   并发数--tomcat

3、业务层

    缓存数据

4、数据库层

    数据库连接池, 性能监控, 调优

   》c3p0的监控

      tomcat通过配置jmx可以监控  (资料来源于网络)  

Step1: 配置Tomcat支持JMX
在$CATALINA_HOME/bin中找到catalina.sh或者catalina.bat文件,在该文件中添加jmx的配置
以catalina.bat为例:
set CATALINA_OPTS=%CATALINA_OPTS% -Dcom.sun.management.jmxremote
set CATALINA_OPTS=%CATALINA_OPTS% -Dcom.sun.management.jmxremote.port=9999
set CATALINA_OPTS=%CATALINA_OPTS% -Dcom.sun.management.jmxremote.authenticate=false
set CATALINA_OPTS=%CATALINA_OPTS% -Dcom.sun.management.jmxremote.ssl=false
表示jmx开放端口是9999
配置完以后重启tomcat服务。
Step2:使用Jconsole监控
编写脚本monitor.bat
内容是
jconsole -J-Djava.class.path=%JAVA_HOME%/lib/jconsole.jar;%JAVA_HOME%/lib/tools.jar;c3p0-0.9.1.jar
Step3:查看结果
在jconsole的对话框中输入<hostname>:<port>进入Mbean查看c3p0的信息。
如果jconsole连接不上<hostname>:<port>,而telnet可以连接上<hostname>:<port>,那么需要确认在服务器中执行hostname -i看结果是不是你使用<hostname>,如果不是的话要修改/etc/hosts内容。



注明:如果是本机监控的的话, 其实只要直接启动Jconsole,监控tomcat就ok了。 c3p0默认就是jmx方式


》用jsp编程的方式监控jmx(来自网络, 这个好像c3p0的例子)

<%@ page contentType="text/html;charset=GBK"%>    
<%@ page import="java.sql.*"%>    
<%@ page import="javax.sql.*"%>  
<%@ page import="javax.naming.*"%>  
<%@ page import="com.mchange.v2.c3p0.*"%>    
<%!  
    int numConnections=0;  
    int numBusyConnections=0;  
    int numIdleConnections=0;  
    int numUnclosedOrphanedConnections=0;  
%>  
<%         
    InitialContext ictx = new InitialContext();  
        DataSource ds=null;  
    PooledDataSource pds=null;  
    try {    
    ictx = new InitialContext();  
        ds = (DataSource) ictx.lookup("java:comp/env/jdbc/igrp" );    
     
    if ( ds instanceof PooledDataSource) {     
        pds = (PooledDataSource) ds;   
        String clear=request.getParameter("clear");  
      
        if("1".equals(clear)){  
            pds.hardReset();  
        }  
        numConnections= pds.getNumConnectionsDefaultUser();     
        numBusyConnections= pds.getNumBusyConnectionsDefaultUser();  
        numIdleConnections= pds.getNumIdleConnectionsDefaultUser();   
        numUnclosedOrphanedConnections= pds.getNumUnclosedOrphanedConnectionsDefaultUser();   
    }   
    else   
        System.err.println("Not a c3p0 PooledDataSource!");    
    } catch (Exception ex) {    
        ex.printStackTrace();  
    }    
      
%>    
<body align="center">  
    <table border="1" align="center">  
        <tr>  
            <td>总的连接数</td>  
            <td><%=numConnections%></td>  
        </tr>  
        <tr>  
            <td>正在运行状态的连接数</td>  
            <td><%=numBusyConnections%></td>  
        </tr>  
        <tr>  
            <td>空闲连接数</td>  
            <td><%=numIdleConnections%></td>  
        </tr>  
        <tr>  
            <td>都是checkoutconnection,但他们已经不在池中管理了.当他们checkin时候,将被destory</td>  
            <td><%=numUnclosedOrphanedConnections%></td>  
        </tr>  
    </table>  
  
    <form action="c3p0.jsp" method="post">  
        <input type="hidden" id="clear" name="clear" value="1"/>  
        <input type="submit" value="清空连接池"/>  
    </form>  
</body>  



     

5、系统测试

6、系统监控、问题扑捉、 调优

   验证稳定

   验证效率

  系统监控

    linux: jmap

    window: 


工具:LR,apache, httpwatch



1、sql语句监控的系统, 包括语句执行的频度, 时间等等, 页面打开时间, 系统的健康程度, 系统运行时间、总访问次数、各种业务的分类统计数据

                                            访问次数, 访问时间范围、 java内存消耗, cpu占用等等。


2、用户行为收集的东东, 用户打开页面的统计, 用户页面停留时间, 页面数据大小 , 可以知道用户操作的习惯, 为系统以后的升级做出更好的修正提供必要的信息


系统监控的



附录:


tomcat并发设置

在tomcat配置文件server.xml中的<Connector ... />配置中,和连接数相关的参数有: 
minProcessors:最小空闲连接线程数,用于提高系统处理性能,默认值为10 
maxProcessors:最大连接线程数,即:并发处理的最大请求数,默认值为75 
acceptCount:允许的最大连接数,应大于等于maxProcessors,默认值为100 
enableLookups:是否反查域名,取值为:true或false。为了提高处理能力,应设置为false 
connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 

其中和最大连接数相关的参数为maxProcessors和acceptCount。如果要加大并发连接数,应同时加大这两个参数。


  1. <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"  
  2.                connectionTimeout="20000"  
  3.                redirectPort="8443" />  


JVM设置 
堆的尺寸 
-Xmssize in bytes 
    设定Java堆的初始尺寸,缺省尺寸是2097152 (2MB)。这个值必须是1024个字节(1KB)的倍数,且比它大。(-server选项把缺省尺寸增加到32M。) 
-Xmnsize in bytes 
    为Eden对象设定初始Java堆的大小,缺省值为640K。(-server选项把缺省尺寸增加到2M。) 
-Xmxsize in bytes 
    设定Java堆的最大尺寸,缺省值为64M,(-server选项把缺省尺寸增加到128M。) 最大的堆尺寸达到将近2GB(2048MB)。 

请注意:很多垃圾收集器的选项依赖于堆大小的设定。请在微调垃圾收集器使用内存空间的方式之前,确认是否已经正确设定了堆的尺寸。 

垃圾收集:内存的使用 
-XX:MinHeapFreeRatio=percentage as a whole number 
    修改垃圾回收之后堆中可用内存的最小百分比,缺省值是40。如果垃圾回收后至少还有40%的堆内存没有被释放,则系统将增加堆的尺寸。 
-XX:MaxHeapFreeRatio=percentage as a whole number 
    改变垃圾回收之后和堆内存缩小之前可用堆内存的最大百分比,缺省值为70。这意味着如果在垃圾回收之后还有大于70%的堆内存,则系统就会减少堆的尺寸。 
-XX:NewSize=size in bytes 
    为已分配内存的对象中的Eden代设置缺省的内存尺寸。它的缺省值是640K。(-server选项把缺省尺寸增加到2M。) 
-XX:MaxNewSize=size in bytes 
    允许您改变初期对象空间的上限,新建对象所需的内存就是从这个空间中分配来的,这个选项的缺省值是640K。(-server选项把缺省尺寸增加到2M。) 
-XX:NewRatio=value 
    改变新旧空间的尺寸比例,这个比例的缺省值是8,意思是新空间的尺寸是旧空间的1/8。 
-XX:SurvivorRatio=number 
    改变Eden对象空间和残存空间的尺寸比例,这个比例的缺省值是10,意思是Eden对象空间的尺寸比残存空间大survivorRatio+2倍。 
-XX:TargetSurvivorRatio=percentage 
    设定您所期望的空间提取后被使用的残存空间的百分比,缺省值是50。 
-XX:MaxPermSize=size in MB 
    长久代(permanent generation)的尺寸,缺省值为32(32MB)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值