网站缓存技术

  

文章转自:http://dcross.iteye.com/blog/721468

  网站技术高速发展的今天,缓存技术已经成为大型网站的一个关键技术,缓存设计好坏直接关系的一个网站访问的速度,以及购置服务器的数量,甚至影响到用户的体验。

网站缓存按照存放的地点不同,可以分为客户端缓存、服务端缓存。

客户端缓存

客户端缓存又可分为:浏览器缓存、网关或代理服务器缓存
网关或代理服务器缓存是将网页缓存中网关服务器上,多用户访问同一个页面时,将直接从网关服务器把页面传送给用户。
浏览器缓存是最靠近用户的缓存,如果启用缓存,用户在访问同一个页面时,将不再从服务器下载页面,而是从本机的缓存目录中读取页面,然后再浏览器中展现这个页面。
浏览器缓存的控制,可以设置meta标签,可以设置数字,也可以设置时间,如下:
<Meta http-equiv=”Expires” Content=”3600″>
<Meta http-equiv=”Expires” Content=”Wed, 26 Feb 1997 08:21:57 GMT”>
HTTP头信息如下:
HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
不过现在的网站为了保证用户访问到最新的内容,一般很少采用浏览器缓存,取而代之的是更加灵活的服务器缓存。

服务端缓存

服务端缓存分为:页面缓存、数据缓存、数据库缓存

1、页面缓存

页面缓存是将动态页面直接生成静态的页面放在服务器端,用户调取相同页面时,静态页面将直接下载到客户端,不再需要通过程序的运行和数据库的访问,大大节约了服务器的负载。
早期的网站很多使用发布系统来完成这个功能,在后台发布时将数据和页面模板整合成静态页面,存放在硬盘中。但这样的缺陷很明显,一是后台的程序的编写很复杂,二是缓存的控制只能通过人为的方式来控制,这对一些更新十分频繁的网站就是一个噩梦,网站可能在不停的做缓存的删除和重建。当然后来出现了一些自动更新这些缓存的框架,比如PHP的SMARTY模板技术,可以定义缓存过期的时间,自动去更新这些缓存。这对一些信息发布类网站已经确实适用了。
除了整个页面的缓存技术,还有一种技术叫做“网页片段缓存技术”,将页面的部分而不是全部进行缓存。代表作有ESI cache。

2、数据缓存

但是当WEB2.0兴起的今天,信息的发布已经不再是管理员统一发布的了,而是所有的用户都在发布信息,用户发布完信息后当然是想看到这些信息,而不是等到缓存时间到刷新后才看到这些数据,于是数据缓存的相关技术也就应运而生了。
比较有名的数据缓存框架有ehcache和 memcached。
ehcache有很多缓存的分支(包括页面缓存的模块),但最核心的模块还是它的数据缓存部分,比如,当ehcache和hibernate进行整合时,能将查询出的对象集合放入内存中,下次如果再查询这个查询,将直接从内存中返回这个数据集合,不需要再进行数据库的查询,同时,你可以配置缓存的刷新模式,有read-only,nonstrict-read-write,read-write 几种模式,其中read-only表示缓存是不刷新的(要刷新就只有重启了),nonstrict-read-write表示刷新是不及时的,你可以设置超时的时间去刷新,read-write表示在数据发生变化时缓存都会发生刷新,具体怎么配置可能就要根据具体业务了。
Memcached大致的原理也和ehcache 相同,将数据采用键值的形式存放在内存中,使用时可以将查询的md5作为键,查询的结果作为值。相对ehcache而言,memcached是一个工具,ehcache是一个框架,memcached更加底层更加灵活,当然你也要写相应的代码去使用它。
这是一张网上的memcached图,说明了memcached在系统中的位置。


近几年兴起的NOSQL技术,虽然现在归于数据库的一种,但其本质也是缓存技术和数据库技术的一种融合产物。
目前缓存的做法分为两种模式:
内存缓存:缓存数据存放在服务器的内存空间中。
优点:速度快 缺点:资源有限
文件缓存:缓存数据存放在服务器的硬盘空间中。
优点:容量大 缺点:速度偏慢,尤其在缓存数量巨大时

数据库缓存

数据库的缓存一般由数据库提供,比如ORACLE,可以对表建立高速缓存,提高对经常访问的数据的访问速度。

总结

究竟怎样去使用缓存,使用哪一层次的缓存,是由网站本身的具体业务来决定的。缓存技术的一个原则是:让数据更靠近用户。缓存技术是一门博大精深的艺术,我也是只知些皮毛。

JDBC SQL占位符问题

之前一直以为JDBC占位符一直是?,今天发现在Oracle中可以用:abc来表示

如下代码:

 

public class ExcelClient {
 
    /**
     * @param args
     */
    public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver";
    //连接地址是由各个数据库生产商单独提供的,所以需要单独记住
    public static final String DBURL = "jdbc:oracle:thin:@172.18.50.139:1521:movitechOcl";
    //连接数据库的用户名
    public static final String DBUSER = "space_sm";
    //连接数据库的密码
    public static final String DBPASS = "space_sm";
 	
	public static void main(String[] args) throws Exception {
        Connection con = null; //表示数据库的连接对象
        PreparedStatement pstmt = null; //表示数据库更新操作

        String sql = "SELECT * from sm_scenario t where t.id = :id"; //正确

        Class.forName(DBDRIVER); //1、使用CLASS 类加载驱动程序


        con = DriverManager.getConnection(DBURL, DBUSER, DBPASS); //2、连接数据库
        pstmt = con.prepareStatement(sql); //使用预处理的方式创建对象
        pstmt.setString(1,"1419328510900");

        ResultSet rs = pstmt.executeQuery();
        if(rs.next()) {
            System.out.println(rs.getString(1));

        }

        pstmt.close();
        con.close(); // 4、关闭数据库
    }
}

结论不同数据的驱动不一样,占位符的定义也肯不相同。

1. 在Oracle中占位符可以是?或者:abc

 

2. 在MySQL中站位符只能是?

spring集成quartz入门配置

    Quartz是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能, 但就其简易形式看,你会发现它易用得简直让人受不了!。简单地创建一个实现org.quartz.Job接口的java类。Job接口包含唯一的方法:
public void execute(JobExecutionContext context)
throws JobExecutionException;
在你的Job接口实现类里面,添加一些逻辑到execute()方法。一旦你配置好Job实现类并设定好调度时间表,Quartz将密切注意剩余时间。当调度程序确定该是通知你的作业的时候,Quartz框架将调用你Job实现类(作业类)上的execute()方法并允许做它该做的事情。无需报告任何东西给调度器或调用任何特定的东西。仅仅执行任务和结束任务即可。如果配置你的作业在随后再次被调用,Quartz框架将在恰当的时间再次调用它。

maven

 <dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.1.7</version>
</dependency> 

spring-quartz.xml

<bean name="job1"  
        class="org.springframework.scheduling.quartz.JobDetailFactoryBean">  
        <property name="jobClass" value="com.movit.test.BusinessJob" />  
        <property name="durability" value="true"></property> 
    </bean>  
    
  	 <!-- 触发器 -->  
    <bean id="cronTrigger"  
        class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">  
  
        <!-- 指向我们的任务 -->  
        <property name="jobDetail" ref="job1" />  
  
        <property name="cronExpression" value="0 0/1 * * * ?" />  
       
    </bean>  
    
      <bean  
        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">  
        <property name="triggers">  
            <list>  
                <!--  触发器列表 -->  
                <ref bean="cronTrigger" />  
            </list>  
        </property>  
    </bean>  

BusinessJob.java

public class BusinessJob extends QuartzJobBean{

	@Override
	protected void executeInternal(JobExecutionContext jobexecutioncontext)
			throws JobExecutionException {
		 
		System.out.println("execute hello!!");
	}
 
}

认识消息中间件

原文转载http://www.uml.org.cn/j2ee/2009008071.asp

1、消息中间件是什麽 

   消息中间件是指利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。 
消息中间件可以即支持同步方式,又支持异步方式。异步中间件比同步中间件具有更强的容错性,在系 统故障时可以保证消息的正常传输。异步中间件技术又分为两类:广播方式和发布/订阅方式。由于发布/订阅方式可以指定哪种类型的用户可以接受哪种类型的消 息,更加有针对性,事实上已成为异步中间件的非正式标准。 

2、消息中间件原理 

    面向消息的中间件(MOM),提供了以松散耦合的灵活方式集成应用程序的一种机制。它们提供了基 于存储和转发的应用程序之间的异步数据发送,即应用程序彼此不直接通信,而是与作为中介的MOM通信。MOM提供了有保证的消息发送(至少是在尽可能地做 到这一点),应用程序开发人员无需了解远程过程调用(PRC)和网络/通信协议的细节。 

2.1 消息中间件简介 

    消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。 
    消息中间件适用于需要可靠的数据传送的分布式环境。采用消息中间件机制的系统中,不同的对象之间通过传递消息来激活对方的事件,完成相应的操作。发送者将 消息发送给消息服务器,消息服务器将消息存放在若干队列中,在合适的时候再将消息转发给接收者。消息中间件能在不同平台之间通信,它常被用来屏蔽掉各种平 台及协议之间的特性,实现应用程序之间的协同,其优点在于能够在客户和服务器之间提供同步和异步的连接,并且在任何时刻都可以将消息进行传送或者存储转 发,这也是它比远程过程调用更进一步的原因。 
如下图所示,应用程序A与应用程序B通过使用 MOM 的应用程序编程接口(API)发送消息进行通信。 
 
    MOM将消息路由给应用程B,这样消息就可以存在于完全不同的计算机上,MOM 负责处理网络通信。如果网络连接不可用,MOM会存储消息,直到连接变得可用时,再将消息转发给应用程序B。 
    灵活性的另一方面体现在,当应用程序A发送其消息时,应用程序B甚至可以不处于执行状态。MOM将保留这个消息,直到应用程序B开始执行并试着检索消息为止。这还防止了应用程序A因为等待应用程序B检索消息而出现阻塞。 这种异步通信要求应用程序的设计与现在大多数应用程序不同,不过,对于时间无关或并行处理,它可能是一个极其有用的方法。 

2.2 消息中间件与分布式对象调用的比较 

    分布式对象调用,如CORBA,RMI和DCOM,提供了一种通讯机制,透明地在异构的分布式计 算环境中传递对象请求,而这些对象可以位于本地或远程机器。它通过在对象与对象之间提供一种统一的接口,使对象之间的调用和数据共享不再关心对象的位置、 实现语言及所驻留的操作系统。这个接口就是面向对象的中间件。 
尽管面向对象的中间件是一种很强大的规范被广泛应用,但是面对大规模的复杂分布式系统,这些技术也显示出了局限性: 
1.同步通信:客户发出调用后,必须等待服务对象完成处理并返回结果后才能继续执行。 
2.客户和服务对象的生命周期紧密耦合:客户进程和服务对象进程都必须正常运行,如果由于服务对象崩溃或网络故障导致客户的请求不可达,客户会接收到异常。 
    为了解决这些问题,出现了面向消息的中间件,它较好地解决了以上的问题。 
消息中间件作为一个中间层软件,它为分布式系统中创建、发送、接收消息提供了一套可靠通用的方 法,实现了分布式系统中可靠的、高效的、实时的跨平台数据传输。消息中间件减少了开发跨平台和网络协议软件的复杂性,它屏蔽了不同操作系统和网络协议的具 体细节,面对规模和复杂度都越来越高的分布式系统,消息中间件技术显示出了它的优越性: 
1.采用异步通信模式:发送消息者可以在发送消息后进行其它的工作,不用等待接收者的回应,而接收者也不必在接到消息后立即对发送者的请求进行处理; 
2.客户和服务对象生命周期的松耦合关系:客户进程和服务对象进程不要求都正常运行,如果由于服务对象崩溃或者网络故障导致客户的请求不可达,客户不会接收到异常,消息中间件能保证消息不会丢失。 
2.3 消息中间件的传递模式 
    消息中间件一般有两种传递模型:点对点模型(PTP)和发布-订阅模型(Pub/Sub)[2]。 
2.3.1. 点对点模型(PTP) 
    点对点模型用于消息生产者和消息消费者之间点到点的通信。消息生产者将消息发动到由某个名字标识 的特定消费者。这个名字实际上对应于消息服务中的一个队列(Queue),在消息传动给消费者之前它被存储在这个队列中。队列可以是持久的,以保证在消息 服务出现故障时仍然能够传递消息。 
2.3.2. 发布-订阅模型(Pub/Sub) 
    发布-订阅模型用称为主题(topic)的内容分层结构代替了PTP模型中的惟一目的地,发送应 用程序发布自己的消息,指出消息描述的是有关分层结构中的一个主题的信息。希望接收这些消息的应用程序订阅了这个主题。订阅包含子主题的分层结构中的主题 的订阅者可以接收该主题和其子主题发表的所有消息。 
下图展示了发布和订阅模型: 


   多个应用程序可以就一个主题发布和订阅消息,而应用程序对其他人仍然是匿名的。MOM 起着代理(broker)的作用,将一个主题已发表的消息路由给该主题的所有订阅者。 

2.4 消息中间件产品与JMS 

    从上个世纪90年代初,随着不同厂商消息中间件大量上市,消息中间件技术得到了长足的发展。目 前,IBM和BEA的中间件产品在银行、证券、电信等高端行业,以及IT等行业中得到广泛应用。IBM凭借其在1999年推出的应用服务器 WebSphere,扎根金融、证券等行业,在超大型以及系统整合型应用方面优势突出;BEA则是专门从事中间件开发的公司,它的应用服务器 WebLogic在美国市场占有率超过60%,在国内电信及证券行业占据主要地位;Sun、Oracle、Sybase和Borland等厂商也都有自己 的应用服务器;近年来,以金蝶、东方通等公司为代表的国产中间件产品也发展迅速。[3] 
   由于没有统一的规范和标准,基于消息中间件的应用不可移植,不同的消息中间件也不能互操作,这大 大阻碍了消息中间件的发展。 Java Message Service(JMS, Java消息服务)是SUN及其伙伴公司提出的旨在统一各种消息中间件系统接口的规范。它定义了一套通用的接口和相关语义,提供了诸如持久、验证和事务的 消息服务,它最主要的目的是允许Java应用程序访问现有的消息中间件。JMS规范没有指定在消息节点间所使用的通讯底层协议,来保证应用开发人员不用与 其细节打交道,一个特定的JMS实现可能提供基于TCP/IP、HTTP、UDP或者其它的协议。 
   目前许多厂商采用并实现了JMS API,现在,JMS产品能够为企业提供一套完整的消息传递功能,下面是一些比较流行的JMS商业软件和开源产品。 
1.IBM MQSeries 
        IBM MQ系列产品提供的服务使得应用程序可以使用消息队列进行相互交流,通过一系列基于Java的API,提供了MQSeries在Java中应用开发的方 法。它支持点到点和发布/订阅两种消息模式,在基本消息服务的基础上增加了结构化消息类,通过工作单元提供数据整合等内容。 
2.WebLogic 
        WebLogic是BEA公司实现的基于工业标准的J2EE应用服务器,支持大多数企业级JavaAPI,它完全兼容JMS规范,支持点到点和发布/订阅消息模式,它具有以下一些特点: 
1) 通过使用管理控制台设置JMS配置信息; 
2) 支持消息的多点广播; 
3) 支持持久消息存储的文件和数据库; 
4) 支持XML消息,动态创建持久队列和主题。 
3.SonicMQ 
       SonicMQ是Progress公司实现的JMS产品。除了提供基本的消息驱动服务之外,SonicMQ也提供了很多额外的企业级应用开发工具包,它具有以下一些基本特征: 
1) 提供JMS规范的完全实现,支持点到点消息模式和发布/订阅消息模式; 
2) 支持层次安全管理; 
3) 确保消息在Internet上的持久发送; 
4) 动态路由构架(DRA)使企业能够通过单个消息服务器动态的交换消息; 
5) 支持消息服务器的集群。 
4.Active MQ 
       Active MQ是一个基于Apcache 2.0 licenced发布,开放源码的JMS产品。其特点为: 
1) 提供点到点消息模式和发布/订阅消息模式; 
2) 支持JBoss、Geronimo等开源应用服务器,支持Spring框架的消息驱动; 
3) 新增了一个P2P传输层,可以用于创建可靠的P2P JMS网络连接; 
4) 拥有消息持久化、事务、集群支持等JMS基础设施服务。 
5.OpenJMS 
       OpenJMS是一个开源的JMS规范的实现,它包含以下几个特征: 
1) 它支持点到点模型和发布/订阅模型; 
2) 支持同步与异步消息发送; 
3) 可视化管理界面,支持Applet; 
4) 能够与Jakarta Tomcat这样的Servlet容器结合; 
5) 支持RMI、TCP、HTTP与SSL协议。 

如何正确地处理时间

转载:http://www.liaoxuefeng.com/article/0014132675721847f569c3514034f099477472c73b5dee2000

日期和时间在程序中应用广泛,每种程序开发语言都自带处理日期和时间的相关函数,很多开发者把日期和时间存入数据库中,但是,一旦涉及到跨时区的日期和时间的处理时,大多数开发者根本就不明白如何正确地处理日期和时间。

首先,我们来看大部分的程序都是这么创建当前时间并存入数据库的:

Date date = new Date();
store2db(date);

这么做的问题在于,数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息。如果你把数据库服务器的时区改了,或者把应用服务器的时区改了,读出来的日期和时间就是错误的。如果以Timestamp类型存储,各数据库的实现也不相同,有的进行了内部时区自动转换,而且,存储的时间不超过2037年。

如果应用服务器的时区和数据库服务器的时区不一致,你无法确定数据库驱动程序会不会自动帮你转换。

大多数开发者遇到这个问题不是去探索正确的解决方法,而是自作聪明地在存入数据库之前先来个“调整”,比如把当前时间减掉8小时,在显示的时候遇到不正确的时间时,又来个“调整”,以“负负得正”的方式来掩盖错误。在遇到夏令时的时区时,还需要写更复杂的代码来调整小时。

正确的做法是先理解时间和时区的概念。

时区的概念

之所以有时区的概念是因为住在地球上不同地方的人看到太阳升起的时间是不一样的。我们假设北京人民在早上8:00看到了太阳刚刚升起,而此刻欧洲人民还在夜里,他们还需要再过7个小时才能看到太阳升起,所以,此刻欧洲人民的手表上显示的是凌晨1:00。如果你强迫他们用北京时间那他们每天看到日出的时间就是下午3点。

也就是说,东8区的北京人民的手表显示的8:00和东1区欧洲人民手表显示的1:00是相同的时刻:

"2014-10-14 08:00 +8:00" = "2014-10-14 01:00 +1:00"

这就是本地时间的概念。

但是,在计算机中,如果用本地时间来存储日期和时间,在遇到时区转换的问题上,即便你非常清楚地知道如何转换,也非常麻烦,尤其是矫情的美国人还在采用夏令时。

所以我们需要引入“绝对时间”的概念。绝对时间不需要年月日,而是以秒来计时。当前时间是指从一个基准时间(1970-1-1 00:00:00 +0:00),到现在的秒数,用一个整数表示。

当我们用绝对时间表示日期和时间时,无论服务器在哪个时区,任意时刻,他们生成的时间值都是相等的。所有编程语言都提供了方法来生成这个时间戳,Java和JavaScript输出以毫秒计算的Long型整数,Python等输出标准的Unix时间戳,以秒计算的Float型浮点数,这两者转换只存在1000倍的关系。

实际上,操作系统内部的计时器也是这个标准的时间戳,只有在显示给用户的时候,才转换为字符串格式的本地时间。

正确的存储方式

基于“数据的存储和显示相分离”的设计原则,我们只要把表示绝对时间的时间戳(无论是Long型还是Float)存入数据库,在显示的时候根据用户设置的时区格式化为正确的字符串。

数据的存储和显示相分离是非常基本的设计原则,却常常被大多数开发人员忽略。举个例子,在Excel中编写一个表格,表格的数据可视为数据的存储格式,你可以把表格的数据以柱状图或饼图表示出来,这些不同的图表是数据的不同显示格式,存储数据的时候,我们应该存储表格数据,绝不应该存储柱状图等图片信息。

HTML和CSS也是数据的存储和显示相分离的设计思想。

所以,数据库存储时间和日期时,只需要把Long或者Float表示的时间戳存到BIGINTREAL类型的列中,完全不用管数据库自己提供的DATETIMETIMESTAMP,也不用担心应用服务器和数据库服务器的时区设置问题,遇到Oracle数据库你不必去理会with timezonewith local timezone到底有啥区别。

读取时间时,读到的是一个Long或Float,只需要按照用户的时区格式化为字符串就能正确地显示出来:

// Java:
long t = System.currentTimeMillis();
System.out.println("long = " + t);

// current time zone:
SimpleDateFormat sdf_default = new SimpleDateFormat("yyyy-MM-dd HH:mm");
System.out.println(sdf_default.format(t));

// +8:00 time zone:
SimpleDateFormat sdf_8 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_8.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
System.out.println("GMT+8:00 = " + sdf_8.format(t));

// +7:00 time zone:
SimpleDateFormat sdf_7 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_7.setTimeZone(TimeZone.getTimeZone("GMT+7:00"));
System.out.println("GMT+7:00 = " + sdf_7.format(t));

// -9:00 time zone:
SimpleDateFormat sdf_la = new SimpleDateFormat("yyyy-MM-dd HH:mm");
sdf_la.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println("America/Los_Angeles = " + sdf_la.format(t));

输出:

long = 1413230086802
2014-10-14 03:54
GMT+8:00 = 2014-10-14 03:54
GMT+7:00 = 2014-10-14 02:54
America/Los_Angeles = 2014-10-13 12:54

基于绝对时间戳的时间存储,从根本上就没有时区的问题。时区只是一个显示问题。额外获得的好处还包括:

  • 两个时间的比较就是数值的比较,根本不涉及时区问题,极其简单;

  • 时间的筛选也是两个数值之间筛选,写出SQL就是between(?, ?)

  • 显示时间时,把Long或Float传到页面,无论用服务端脚本还是用JavaScript都能简单而正确地显示时间。

你唯一需要编写的两个辅助函数就是String->LongLong->StringString->Long的作用是把用户输入的时间字符串按照用户指定时区转换成Long存进数据库。

唯一的缺点是数据库查询你看到的不是时间字符串,而是类似1413266801750之类的数字。