转自:http://www.javabloger.com/java-development-concern-those-things/html
近期各家IT媒体举办的业内技术大会让不少网站都在披露本身的技术内幕与同行们分享,大到facebook,百度,小到刚起步的网站。facebook,百度之类的大型网站采用的技术和超凡的处理能力的确给人耳目一新的感受,但并非每一个网站都是像facebook,百度 有上亿的用户访问流量,有海量的数据须要存储,须要使用到mapreduce/并行计算,HBase/列存储这些技术不可。技术手段始终是运营的支撑,对于当前的运营环境适用就好,没有必要非要赶个时髦,必定要和某项流行的技术产生点关系才善罢甘休。前端
在最近的技术大会中咱们更多的目光都聚焦在这些大型网站,其实中小型门户网站的技术体系也是值得去探讨和关注。全天下的攻城师们并非都在为这些大型门户网站服务,更多的攻城师们正在默默无闻的为一些刚刚起步的中小型网站服务,并且占据了攻城师队伍中的60%以上的人群。在关注大型门户网站的时候,中小型门户网站的技术发展和实战经验更值得去分享。java
不管大型门户网站仍是中小型垂直类型网站都会对稳定性、性能和可伸缩性有所追求。大型网站的技术经验分享值得咱们去学习和借用,但落实到更具体的实践上并非对全部网站能够适用,其余语言开发的网站我还不敢多说,但Java开发的系统,我仍是能您给插上几句话:mysql
JVM
JEE容器中运行的JVM参数配置参数的正确使用直接关系到整个系统的性能和处理能力,JVM的调优主要是对内存管理方面的调优,优化的方向分为如下4点:
1.HeapSize 堆的大小,也能够说Java虚拟机使用内存的策略,这点是很是关键的。
2.GarbageCollector 经过配置相关的参数进行Java中的垃圾收集器的4个算法(策略)进行使用。
3.StackSize 栈是JVM的内存指令区,每一个线程都有他本身的Stack,Stack的大小限制着线程的数量。
4.DeBug/Log 在JVM中还能够设置对JVM运行时的日志和JVM挂掉后的日志输出,这点很是的关键,根据各种JVM的日志输出才能配置合适的参数。
网上随处可见JVM的配置技巧,可是我仍是推荐阅读Sun官方的2篇文章,能够对配置参数的其所依然有一个了解
1.Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
2.Troubleshooting Guide for Java SE 6 with HotSpot VMhttp://www.oracle.com/technetwork/java/javase/index-137495.html
另外,我相信不是每一个人攻城师都是每天对着这些JVM参数的,若是你忘记了那些关键的参数你能够输入Java -X(大写X)进行提示。web
JDBC
针对MySQL的JDBC的参数在以前的文章中也有介绍过,在单台机器或者集群的环境下合理的使用JDBC中的配置参数对操做数据库也有很大的影响。
一些所谓高性能的 Java ORM开源框架也就是打开了不少JDBC中的默认参数:
1.例如:autoReconnect、prepStmtCacheSize、cachePrepStmts、useNewIO、blobSendChunkSize 等,
2.例如集群环境下:roundRobinLoadBalance、failOverReadOnly、autoReconnectForPools、secondsBeforeRetryMaster。
具体内容能够参阅MySQL的JDBC官方使用手册:
http://dev.mysql.com/doc/refman/5.1/zh/connectors.html#cj-jdbc-referenceredis
数据库链接池(DataSource)
应用程序与数据库链接频繁的交互会给系统带来瓶颈和大量的开销会影响到系统的性能,JDBC链接池负责分配、管理和释放数据库链接,它容许应用程序重复使用一个现有的数据库链接,而再不是从新创建一个链接,所以应用程序不须要频繁的与数据库开关链接,而且能够释放空闲时间超过最大空闲时间的数据库链接来避免由于没有释放数据库链接而引发的数据库链接遗漏。这项技术能明显提升对数据库操做的性能。
在此我认为有一点须要说明:
链接池的使用也是须要关闭,由于在数据库链接池启动的时候就预先和数据库得到了相应的链接,以后再也不须要应用程序直接的和数据库打交道,由于应用程序使用数据库链接池是一个“借”的概念,应用程序从数据库链接池中得到资源是“借出”,还须要还回去,就比如有20个水桶放在这里,须要拿水的人均可以使用这些木桶从水池里面拿水,若是20我的都拿完水,不将水桶还回原地,那么后面来的人再须要拿水,只能在旁边等待有人将木桶还回去,以前的人用完后须要放回去,否则后面的人就会一直等待,形成资源堵塞,同理,应用程序获取数据库链接的时候Connection链接对象的时候是从“池”中分配一个数据库链接出去,在使用完毕后,归还这个数据库链接,这样才能保持数据库的链接“有借有还”准则。
参考资料:
http://dev.mysql.com/doc/refman/5.1/zh/connectors.html#cj-connection-pooling算法
数据存取
数据库服务器的优化和数据的存取,什么类型的数据放在什么地方更好是值得去思考的问题,未来的存储极可能是混用的,Cache,NOSQL,DFS,DataBase 在一个系统中都会有,生活的餐具和平日里穿衣服须要摆放在家里,可是不会用同一种类型的家具存放,貌似没有那我的家把餐具和衣服放在同一个柜子里面的。这就像是系统中不一样类型的数据同样,对不一样类型的数据须要使用合适的存储环境。文件和图片的存储,首先按照访问的热度分类,或者按照文件的大小。强关系类型而且须要事务支持的采用传统的数据库,弱关系型不须要事务支持的能够考虑NOSQL,海量文件存储能够考虑一下支持网络存储的DFS,至于缓存要看你单个数据存储的大小和读写的比例。
还有一点值得注意就是数据读写分离,不管在DataBase仍是NOSQL的环境中大部分都是读大于写,所以在设计时还需考虑 不只仅须要让数据的读分散在多台机器上,还须要考虑多台机器之间的数据一致性,MySQL的一主多从,在加上MySQL-Proxy或者借用JDBC中的一些参数(roundRobinLoadBalance、failOverReadOnly、autoReconnectForPools、secondsBeforeRetryMaster)对后续应用程序开发,能够将读和写分离,将大量读的压力分散在多台机器上,而且还保证了数据的一致性。spring
缓存
在宏观上看缓存通常分为2种:本地缓存和分布式缓存
1.本地缓存,对于Java的本地缓存而言就是讲数据放入静态(static)的数据结合中,而后须要用的时候就从静态数据结合中拿出来,对于高并发的环境建议使用 ConcurrentHashMap或者CopyOnWriteArrayList做为本地缓存。缓存的使用更具体点说就是对系统内存的使用,使用多少内存的资源须要有一个适当比例,若是超过适当的使用存储访问,将会拔苗助长,致使整个系统的运行效率低下。
2. 分布式缓存,通常用于分布式的环境,将每台机器上的缓存进行集中化的存储,而且不只仅用于缓存的使用范畴,还能够做为分布式系统数据同步/传输的一种手段,通常被使用最多的就是Memcached和Redis。
数据存储在不一样的介质上读/写获得的效率是不一样的,在系统中如何善用缓存,让你的数据更靠近cpu,下面有一张图你须要永远牢记在内心,来自Google的技术大牛Jeff Dean(Ref)的杰做,如图所示:
并发/多线程
在高并发环境下建议开发者使用JDK中自带的并发包(java.util.concurrent),在JDK1.5之后使用java.util.concurrent下的工具类能够简化多线程开发,在java.util.concurrent的工具中主要分为如下几个主要部分:
1.线程池,线程池的接口(Executor、ExecutorService)与实现类(ThreadPoolExecutor、 ScheduledThreadPoolExecutor),利用jdk自带的线程池框架能够管理任务的排队和安排,并容许受控制的关闭。由于运行一个线程须要消耗系统CPU资源,而建立、结束一个线程也对系统CPU资源有开销,使用线程池不只仅能够有效的管理多线程的使用,仍是能够提升线程的运行效率。
2.本地队列,提供了高效的、可伸缩的、线程安全的非阻塞 FIFO 队列。java.util.concurrent 中的五个实现都支持扩展的 BlockingQueue 接口,该接口定义了 put 和 take 的阻塞版本:LinkedBlockingQueue、ArrayBlockingQueue、SynchronousQueue、PriorityBlockingQueue 和 DelayQueue。这些不一样的类覆盖了生产者-使用者、消息传递、并行任务执行和相关并发设计的大多数常见使用的上下文。
3.同步器,四个类可协助实现常见的专用同步语句。Semaphore 是一个经典的并发工具。CountDownLatch 是一个极其简单但又极其经常使用的实用工具,用于在保持给定数目的信号、事件或条件前阻塞执行。CyclicBarrier 是一个可重置的多路同步点,在某些并行编程风格中颇有用。Exchanger 容许两个线程在 collection 点交换对象,它在多流水线设计中是有用的。
4.并发包 Collection,此包还提供了设计用于多线程上下文中的 Collection 实现:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。当指望许多线程访问一个给定 collection 时,ConcurrentHashMap 一般优于同步的 HashMap,ConcurrentSkipListMap 一般优于同步的 TreeMap。当指望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。sql
队列
关于队列能够分为:本地的队列 和 分布式队列 2类
本地队列:通常常见的用于非及时性的数据批量写入,能够将获取的数据缓存在一个数组中等达到必定数量的时候在进行批量的一次写入,可使用BlockingQueue或者List/Map来实现。
相关资料:Sun Java API.
分布式队列:通常做为消息中间件,构建分布式环境下子系统与子系统之间通讯的桥梁,JEE环境中使用最多的就是Apache的AvtiveMQ和Sun公司的OpenMQ。
轻量级的MQ中间件以前也向你们介绍过一些例如:Kestrel和Redis(Ref http://www.javabloger.com/article/mq-kestrel-redis-for-java.html),最近又据说LinkedIn的搜索技术团队推出了一个MQ产品-kaukaf(Ref http://sna-projects.com/kafka ),对此保持关注。
相关资料:
1.ActiveMQ http://activemq.apache.org/getting-started.html
2.OpenMQ http://mq.java.net/about.html
3.Kafka http://sna-projects.com/kafka
4.JMS文章 http://www.javabloger.com/article/category/jms数据库
NIO
NIO是在JDK1.4后的版本中出现的,在Java 1.4以前,Jdk提供的都是面向流的I/O系统,例如读/写文件则是一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的数据, 面向流的I/O速度很是慢,而且一个数据包要么整个数据报已经收到,要么尚未。Java NIO非堵塞技术实际是采起Reactor模式,有内容进来会自动通知,没必要死等、死循环,大大的提高了系统性能。在现实场景中NIO技术多数运用两个方面,1是文件的读写操做,2是网络上数据流的操做。在NIO中有几个核心对象须要掌握:1选择器(Selector)、2通道(Channel)、3缓冲区(Buffer)。
个人废话:
1.在Java NIO的技术范畴中内存映射文件是一种高效的作法,能够用于缓存中存储的冷/热数据分离,将缓存中的一部分冷数据进行这样的处理,这种作法上比常规的基于流或者基于通道的I/O快的多,经过使文件中的数据出现为内存数组的内容来完成的,将文件中实际读取或者写入的部分才会映射到内存中,并非将整个文件读到内存中。
2.在Mysql的jdbc驱动中也可使用NIO技术对数据库进行操做来提高系统的性能。
长链接/Servlet3.0
这里说的长链接就是长轮询,之前浏览器(客户端)须要关注服务器端发生的数据变化须要不断的访问服务器,这样客户端的数量一多必然会给服务器端形成很大的压力,例如:论坛中的站内消息。如今Servlet3.0规范中提供了一个新的特性:异步IO通讯;该特性会保持一个长链接。利用Servlet3异步请求的这项技术能够大大的缓解服务器端的压力。
Servlet3.0的原理就是将request的请求开启一个线程挂起,中间设置等待超时的时间,若是后台事件触发request请求,获得的结果返回给客户端的request请求,若是在设置等待超时的时间内没有任何事件发生也将请求返回给客户端,客户端将再次发起request请求,客户端与服务器端的交互能够与此往复。
就比如,你先过来跟我说若是有人找你,我就立马通知你你来见他,原先你须要不断的问我有没有要找你,而无论有没有人找你,你都须要不断的问我有没有人找你,这样的话不论问的人仍是被问的人都会累死。
日志
Log4J是一般被人们使用的工具,系统在刚刚上线的时候日志通常都设置在INFO的级,真正上线后通常设置在ERROR级,但不管在任什么时候候,日志的输入内容都是须要关注的,开发人员通常能够依靠输出的日志查找出现的问题或者依靠输出的日志对系统的性能进行优化,日志也是系统运行状态的报告和排错的依据。
简单来讲日志按照定义的不一样策略和等级输出到不一样的环境,那样便于咱们分析和管理。相反你没有策略的输出,那么机器一多,时间一长,会有一大推乱糟糟的日志,会让你排错的时候无从下手,因此日志的输出策略是使用日志的关键点。
参考资料:http://logging.apache.org/log4j/1.2/manual.html
打包/部署
在代码设计的时候最好能将不一样类型的功能模块在IDE环境中粗粒度的分为不一样的工程,便于打成不一样jar包部署在不一样的环境中。有这样的一个应用场景:须要天天定时远程从SP那边得到当天100条新闻和部分城市的天气预报,虽然天天的数据量很少,可是前端访问的并发量很大,显然须要在系统架构上作到读写分离。
若是把web工程和定时抓取的功能模块彻底集中在一个工程里打包,将致使须要扩展的时候每台机器上既有web应用也有定时器,由于功能模块没有分开,每台机器上都有定时器工做将会形成数据库里面的数据重复。
若是开发的时候就将web和定时器分为2个工程,打包的时候就能够分开部署,10台web对应一台按期器,分解了前端请求的压力,数据的写入也不会重复。
这样作的另外一个好处就是能够共用,在上述的场景中web和定时器都须要对数据库进行读取,那么web和定时器的工程里都有操做数据库的代码,在代码的逻辑上仍是感受乱乱的。若是再抽出一个DAL层的jar,web和定时器的应用模块开发者只须要引用DAL层的jar,开发相关的业务逻辑,面向接口编程,无需考虑具体的数据库操做,具体的对数据库操做由其余开发者完成,能够在开发任务分工上很明确,而且互不干涉。
框架
所谓流行的SSH(Struts/Spring/Hiberanet)轻量级框架,对于不少中小型项目而言一点都不轻量级,开发者不只须要维护代码,还须要维护繁琐的xml配置文件,并且说不定某个配置文件写的不对就让整个都工程没法运行。无配置文件能够取代SSH(struts/Spring/Hiberanet)框架的产品真的太多了,我以前就向你们介绍过一些个产品(Ref)。这个我并非一味的反对使用SSH(Struts/Spring/Hiberanet)框架,在我眼里SSH框架真的做用是作到了规范开发,而并不使用了SSH(Struts/Spring/Hiberanet)框架能提升多少性能。SSH框架只是对于很是大的项目人数上百人的团队,还须要、继续增长团队规模的公司而言,是须要选择一些市面上你们都承认,而且熟悉的技术,SSH(Struts/Spring/Hiberanet)框架比较成熟因此是首先产品。可是对于一些小团队中间有个把技术高人的团队而言彻底能够选择更加简洁的框架,真正的作到提速你的开发效率,早日抛弃SSH框架选择更简洁的技术在小团队开发中是一种比较明知的选择。