高吞吐量系统设计优化建议(全文已经发表在IBM开发者论坛)

原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-system-design-optimization/

高吞吐量系统

举一个例子,我们做项目需要安排计划,每一个模块可以由多人同时并行做多项任务,也可以一个人或者多个人串行工作,但始终会有一条关键路径,这条路径就是项目的工期。系统一次调用的响应时间跟项目计划一样,也有一条关键路径,这个关键路径是就是系统影响时间。关键路径由 CPU 运算、IO、外部系统响应等等组成。

对于一个系统的用户来说,从用户点击一个按钮、链接或发出一条指令开始,到系统把结果以用户希望的形式展现出来为终止,整个过程所消耗的时间是用户对这个软件性能的直观印象,也就是我们所说的响应时间。当响应时间较短时,用户体验是很好的,当然用户体验的响应时间包括个人主观因素和客观响应时间。在设计软件时,我们就需要考虑到如何更好地结合这两部分达到用户最佳的体验。如:用户在大数据量查询时,我们可以将先提取出来的数据展示给用户,在用户看的过程中继续进行数据检索,这时用户并不知道我们后台在做什么,用户关注的是用户操作的响应时间。

我们经常说的一个系统吞吐量,通常由 QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降,原因是系统超负荷工作,上下文切换、内存等等其它消耗导致系统性能下降,决定系统响应时间要素。


回页首

缓冲 (Buffer)

缓冲区是一块特定的内存区域,开辟缓冲区的目的是通过缓解应用程序上下层之间的性能差异,提高系统的性能。在日常生活中,缓冲的一个典型应用是漏斗。缓冲可以协调上层组件和下层组件的性能差,当上层组件性能优于下层组件时,可以有效减少上层组件对下层组件的等待时间。基于这样的结构,上层应用组件不需要等待下层组件真实地接受全部数据,即可返回操作,加快了上层组件的处理速度,从而提升系统整体性能。

使用 BufferedWriter 进行缓冲

BufferedWriter 就是一个缓冲区用法,一般来说,缓冲区不宜过小,过小的缓冲区无法起到真正的缓冲作用,缓冲区也不宜过大,过大的缓冲区会浪费系统内存,增加 GC 负担。尽量在 I/O 组件内加入缓冲区,可以提高性能。一个缓冲区例子代码如清单 1 所示。

清单 1. 加上缓冲区之前示例代码
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;

import javax.swing.JApplet;

public class NoBufferMovingCircle extends JApplet implements Runnable{
 Image screenImage = null;
 Thread thread;
 int x = 5;
 int move = 1;

 public void init(){
 screenImage = createImage(230,160);
 }

 public void start(){
 if(thread == null){
 thread = new Thread(this);
 thread.start();
 }
 }

@Override
public void run() {
// TODO Auto-generated method stub
try{
System.out.println(x);
while(true){
x+=move;
System.out.println(x);
if((x>105)||(x<5)){
move*=-1;
}
repaint();
Thread.sleep(10);
}
}catch(Exception e){

}
}

public void drawCircle(Graphics gc){
Graphics2D g = (Graphics2D) gc;
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 100);
g.setColor(Color.red);
g.fillOval(x, 5, 90, 90);
}

public void paint(Graphics g){
g.setColor(Color.white);
g.fillRect(0, 0, 200, 100);
drawCircle(g);
}

}

程序可以完成红球的左右平移,但是效果较差,因为每次的界面刷新都涉及图片的重新绘制,这较为费时,因此,画面的抖动和白光效果明显。为了得到更优质的显示效果,可以为它加上缓冲区。代码如清单 2 所示。

清单 2. 加上缓冲区之后示例代码
import java.awt.Color;
import java.awt.Graphics;

public class BufferMovingCircle extends NoBufferMovingCircle{
 Graphics doubleBuffer = null;//缓冲区

 public void init(){
 super.init();
 doubleBuffer = screenImage.getGraphics();
 }

 public void paint(Graphics g){//使用缓冲区,优化原有的 paint 方法
 doubleBuffer.setColor(Color.white);//先在内存中画图
 doubleBuffer.fillRect(0, 0, 200, 100);
 drawCircle(doubleBuffer);
 g.drawImage(screenImage, 0, 0, this);
 }
}

使用 Buffer 进行 I/O 操作

除 NIO 外,使用 Java 进行 I/O 操作有两种基本方式:

  • 使用基于 InputStream 和 OutputStream 的方式;
  • 使用 Writer 和 Reader。

无论使用哪种方式进行文件 I/O,如果能合理地使用缓冲,就能有效地提高 I/O 的性能。

下面显示了可与 InputStream、OutputStream、Writer 和 Reader 配套使用的缓冲组件。

OutputStream-FileOutputStream-BufferedOutputStream

InputStream-FileInputStream-BufferedInputStream

Writer-FileWriter-BufferedWriter

Reader-FileReader-BufferedReader

使用缓冲组件对文件 I/O 进行包装,可以有效提高文件 I/O 的性能。

清单 3. 示例代码
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class StreamVSBuffer {
 public static void streamMethod() throws IOException{
 try {
long start = System.currentTimeMillis();
//请替换成自己的文件
 DataOutputStream dos = new DataOutputStream(
                        new FileOutputStream("C:\\StreamVSBuffertest.txt"));
for(int i=0;i<10000;i++){
dos.writeBytes(String.valueOf(i)+"\r\n");//循环 1 万次写入数据
}
dos.close();
DataInputStream dis = new DataInputStream(new FileInputStream("C:\\StreamVSBuffertest.txt"));
while(dis.readLine() != null){

}
dis.close();
 System.out.println(System.currentTimeMillis() - start);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

 }

 public static void bufferMethod() throws IOException{
 try {
 long start = System.currentTimeMillis();
 //请替换成自己的文件
 DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
                                        new FileOutputStream("C:\\StreamVSBuffertest.txt")));
 for(int i=0;i<10000;i++){
 dos.writeBytes(String.valueOf(i)+"\r\n");//循环 1 万次写入数据
 }
 dos.close();
 DataInputStream dis = new DataInputStream(new BufferedInputStream(
                                        new FileInputStream("C:\\StreamVSBuffertest.txt")));
 while(dis.readLine() != null){

 }
 dis.close();
 System.out.println(System.currentTimeMillis() - start);
 } catch (FileNotFoundException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }

 public static void main(String[] args){
 try {
StreamVSBuffer.streamMethod();
StreamVSBuffer.bufferMethod();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 }
}

运行结果如清单 4 所示。

清单 4. 运行输出
889
31

很明显使用缓冲的代码性能比没有使用缓冲的快了很多倍。清单 5 所示代码对 FileWriter 和 FileReader 进行了相似的测试。

清单 5.FileWriter 和 FileReader 代码
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class WriterVSBuffer {
 public static void streamMethod() throws IOException{
 try {
long start = System.currentTimeMillis();
 FileWriter fw = new FileWriter("C:\\StreamVSBuffertest.txt");//请替换成自己的文件
for(int i=0;i<10000;i++){
fw.write(String.valueOf(i)+"\r\n");//循环 1 万次写入数据
}
fw.close();
FileReader fr = new FileReader("C:\\StreamVSBuffertest.txt");
while(fr.ready() != false){

}
fr.close();
 System.out.println(System.currentTimeMillis() - start);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

 }

 public static void bufferMethod() throws IOException{
 try {
 long start = System.currentTimeMillis();
 BufferedWriter fw = new BufferedWriter(new FileWriter("C:\\StreamVSBuffertest.txt"));//请替换成自己的文件
 for(int i=0;i<10000;i++){
 fw.write(String.valueOf(i)+"\r\n");//循环 1 万次写入数据
 }
 fw.close();
 BufferedReader fr = new BufferedReader(new FileReader("C:\\StreamVSBuffertest.txt"));
 while(fr.ready() != false){

 }
 fr.close();
 System.out.println(System.currentTimeMillis() - start);
 } catch (FileNotFoundException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 }

 public static void main(String[] args){
 try {
StreamVSBuffer.streamMethod();
StreamVSBuffer.bufferMethod();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
 }
}

运行输出如清单 6 所示。

清单 6. 运行输出
1295
31

从上面例子可以看出,无论对于读取还是写入文件,适当地使用缓冲,可以有效地提升系统的文件读写性能,为用户减少响应时间。


回页首

缓存

缓存也是一块为提升系统性能而开辟的内存空间。缓存的主要作用是暂存数据处理结果,并提供下次访问使用。在很多场合,数据的处理或者数据获取可能会非常费时,当对这个数据的请求量很大时,频繁的数据处理会耗尽 CPU 资源。缓存的作用就是将这些来之不易的数据处理结果暂存起来,当有其他线程或者客户端需要查询相同的数据资源时,可以省略对这些数据的处理流程,而直接从缓存中获取处理结果,并立即返回给请求组件,以此提高系统的响应时间。

目前有很多基于 Java 的缓存框架,比如 EHCache、OSCache 和 JBossCache 等。EHCache 缓存出自 Hibernate,是其默认的数据缓存解决方案;OSCache 缓存是有 OpenSymphony 设计的,它可以用于缓存任何对象,甚至是缓存部分 JSP 页面或者 HTTP 请求;JBossCache 是由 JBoss 开发、可用于 JBoss 集群间数据共享的缓存框架。

以 EHCache 为例,EhCache 的主要特性有:

  1. 快速;
  2. 简单;
  3. 多种缓存策略;
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题;
  5. 缓存数据会在虚拟机重启的过程中写入磁盘;
  6. 可以通过 RMI、可插入 API 等方式进行分布式缓存;
  7. 具有缓存和缓存管理器的侦听接口;
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域;
  9. 提供 Hibernate 的缓存实现。

由于 EhCache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享,这不仅会降低节点运行的效率,而且会导致数据不同步的情况发生。例如某个网站采用 A、B 两个节点作为集群部署,当 A 节点的缓存更新后,而 B 节点缓存尚未更新就可能出现用户在浏览页面的时候,一会是更新后的数据,一会是尚未更新的数据,尽管我们也可以通过 Session Sticky 技术来将用户锁定在某个节点上,但对于一些交互性比较强或者是非
Web 方式的系统来说,Session Sticky 显然不太适合。所以就需要用到 EhCache 的集群解决方案。清单 7 所示是 EHCache 示例代码。

清单 7.EHCache 示例代码
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
/**
 * 第一步:生成 CacheManager 对象
 * 第二步:生成 Cache 对象
 * 第三步:向 Cache 对象里添加由 key,value 组成的键值对的 Element 元素
 * @author mahaibo
 *
 */
public class EHCacheDemo{

public static void main(String[] args) {
 //指定 ehcache.xml 的位置
 String fileName="E:\\1008\\workspace\\ehcachetest\\ehcache.xml";
 CacheManager manager = new CacheManager(fileName);
 //取出所有的 cacheName
 String names[] = manager.getCacheNames();
 for(int i=0;i<names.length;i++){
 System.out.println(names[i]);
 }
 //根据 cacheName 生成一个 Cache 对象
 //第一种方式:
 Cache cache=manager.getCache(names[0]);

 //第二种方式,ehcache 里必须有 defaultCache 存在,"test"可以换成任何值
// Cache cache = new Cache("test", 1, true, false, 5, 2);
// manager.addCache(cache); 

 //向 Cache 对象里添加 Element 元素,Element 元素有 key,value 键值对组成
 cache.put(new Element("key1","values1"));
 Element element = cache.get("key1");

 System.out.println(element.getValue());
 Object obj = element.getObjectValue();
 System.out.println((String)obj);
 manager.shutdown();

 }

}

回页首

对象复用

对象复用池是目前很常用的一种系统优化技术。它的核心思想是,如果一个类被频繁请求使用,那么不必每次都生成一个实例,可以将这个类的一些实例保存在一个“池”中,待需要使用的时候直接从池中获取。这个“池”就称为对象池。在实现细节上,它可能是一个数组,一个链表或者任何集合类。对象池的使用非常广泛,例如线程池和数据库连接池。线程池中保存着可以被重用的线程对象,当有任务被提交到线程时,系统并不需要新建线程,而是从池中获得一个可用的线程,执行这个任务。在任务结束后,不需要关闭线程,而将它返回到池中,以便下次继续使用。由于线程的创建和销毁是较为费时的工作,因此,在线程频繁调度的系统中,线程池可以很好地改善性能。数据库连接池也是一种特殊的对象池,它用于维护数据库连接的集合。当系统需要访问数据库时,不需要重新建立数据库连接,而可以直接从池中获取;在数据库操作完成后,也不关闭数据库连接,而是将连接返回到连接池中。由于数据库连接的创建和销毁是重量级的操作,因此,避免频繁进行这两个操作对改善系统的性能也有积极意义。目前应用较为广泛的数据库连接池组件有
C3P0 和 Proxool。

以 C3P0 为例,它是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,支持 JDBC3 规范和 JDBC2 的标准扩展。目前使用它的开源项目有 Hibernate,Spring 等。如果采用 JNDI 方式配置,如清单 8 所示。

清单 8.Tomcat 数据源配置
<Resource name="jdbc/dbsource"
 type="com.mchange.v2.c3p0.ComboPooledDataSource"
 maxPoolSize="50" minPoolSize="5" acquireIncrement="2" initialPoolSize="10" maxIdleTime="60"
 factory="org.apache.naming.factory.BeanFactory"
 user="xxxx" password="xxxx"
 driverClass="oracle.jdbc.driver.OracleDriver"
 jdbcUrl="jdbc:oracle:thin:@192.168.x.x:1521:orcl"
 idleConnectionTestPeriod="10" />

参数说明:

  1. idleConnectionTestPerio:当数据库重启后或者由于某种原因进程被杀掉后,C3P0 不会自动重新初始化数据库连接池,当新的请求需要访问数据库的时候,此时会报错误 (因为连接失效),同时刷新数据库连接池,丢弃掉已经失效的连接,当第二个请求到来时恢复正常。C3P0 目前没有提供当获取已建立连接失败后重试次数的参数,只有获取新连接失败后重试次数的参数。
  2. acquireRetryAttempts:该参数的作用是设置系统自动检查连接池中连接是否正常的一个频率参数,时间单位是秒。
  3. acquireIncremen:当连接池中的的连接耗尽的时候 c3p0 一次同时获取的连接数,也就是说,如果使用的连接数已经达到了 maxPoolSize,c3p0 会立即建立新的连接。
  4. maxIdleTim:另外,C3P0 默认不会 close 掉不用的连接池,而是将其回收到可用连接池中,这样会导致连接数越来越大,所以需要设置 maxIdleTime(默认 0,表示永远不过期),单位是秒,maxIdleTime 表示 idle 状态的 connection 能存活的最大时间。

如果使用 spring,同时项目中不使用 JNDI,又不想配置 Hibernate,可以直接将 C3P0 配置到 dataSource 中即可,如清单 9 所示。

清单 9.Spring 配置
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass"><value>oracle.jdbc.driver.OracleDriver</value></property>
<property name="jdbcUrl"><value>jdbc:oracle:thin:@localhost:1521:Test</value></property>
<property name="user"><value>Kay</value></property>
<property name="password"><value>root</value></property>
<!--连接池中保留的最小连接数。-->
<property name="minPoolSize" value="10" />
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize" value="100" />
<!--最大空闲时间,1800 秒内未使用则连接被丢弃。若为 0 则永不丢弃。Default: 0 -->
<property name="maxIdleTime" value="1800" />
<!--当连接池中的连接耗尽的时候 c3p0 一次同时获取的连接数。Default: 3 -->
<property name="acquireIncrement" value="3" />
<property name="maxStatements" value="1000" />
<property name="initialPoolSize" value="10" />
<!--每 60 秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->
<property name="acquireRetryAttempts" value="30" />
<property name="breakAfterAcquireFailure" value="true" />
<property name="testConnectionOnCheckout" value="false" />
</bean>

类似的做法存在很多种,用户可以自行上网搜索。


回页首

计算方式转换

计算方式转换比较出名的是时间换空间方式,它通常用于嵌入式设备,或者内存、硬盘空间不足的情况。通过使用牺牲 CPU 的方式,获得原本需要更多内存或者硬盘空间才能完成的工作。

一个非常简单的时间换空间的算法,实现了 a、b 两个变量的值交换。交换两个变量最常用的方法是使用一个中间变量,而引入额外的变量意味着要使用更多的空间。采用下面的方法可以免去中间变量,而达到变量交换的目的,其代价是引入了更多的 CPU 运算。

清单 10. 示例代码
a=a+b;
b=a-b;
a=a-b;

另一个较为有用的例子是对无符号整数的支持。在 Java 语言中,不支持无符号整数,这意味着当需要无符号的 Byte 时,需要使用 Short 代替,这也意味着空间的浪费。下面代码演示了使用位运算模拟无符号 Byte。虽然在取值和设值过程中需要更多的 CPU 运算,但是可以大大降低对内存空间的需求。

清单 11. 无符号整数运算
public class UnsignedByte {
 public short getValue(byte i){//将 byte 转为无符号的数字
 short li = (short)(i & 0xff);
 return li;
 }

 public byte toUnsignedByte(short i){
 return (byte)(i & 0xff);//将 short 转为无符号 byte
 }

 public static void main(String[] args){
 UnsignedByte ins = new UnsignedByte();
 short[] shorts = new short[256];//声明一个 short 数组
 for(int i=0;i<shorts.length;i++){//数组不能超过无符号 byte 的上限
 shorts[i]=(short)i;
 }
 byte[] bytes = new byte[256];//使用 byte 数组替代 short 数组
 for(int i=0;i<bytes.length;i++){
 bytes[i]=ins.toUnsignedByte(shorts[i]);//short 数组的数据存到 byte 数组中
 }
 for(int i=0;i<bytes.length;i++){
 System.out.println(ins.getValue(bytes[i])+" ");//从 byte 数组中取出无符号的 byte
 }
 }
}

运行输出如清单 12 所示,篇幅所限,只显示到 10 为止。

清单 12. 运行输出
0
1
2
3
4
5
6
7
8
9
10

如果 CPU 的能力较弱,可以采用牺牲空间的方式提高计算能力,实例代码如清单 13 所示。

清单 13. 提高计算能力
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class SpaceSort {
 public static int arrayLen = 1000000;

 public static void main(String[] args){
 int[] a = new int[arrayLen];
 int[] old = new int[arrayLen];
 Map<Integer,Object> map = new HashMap<Integer,Object>();
 int count = 0;
 while(count < a.length){
 //初始化数组
 int value = (int)(Math.random()*arrayLen*10)+1;
 if(map.get(value)==null){
 map.put(value, value);
 a[count] = value;
 count++;
 }
 }
 System.arraycopy(a, 0, old, 0, a.length);//从 a 数组拷贝所有数据到 old 数组
 long start = System.currentTimeMillis();
 Arrays.sort(a);
 System.out.println("Arrays.sort spend:"+(System.currentTimeMillis() - start)+"ms");
 System.arraycopy(old, 0, a, 0, old.length);//恢复 原有数据
 start = System.currentTimeMillis();
 spaceTotime(a);
 System.out.println("spaceTotime spend:"+(System.currentTimeMillis() - start)+"ms");
 }

 public static void spaceTotime(int[] array){
 int i = 0;
 int max = array[0];
 int l = array.length;
 for(i=1;i<l;i++){
 if(array[i]>max){
 max = array[i];
 }
 }
 int[] temp = new int[max+1];
 for(i=0;i<l;i++){
 temp[array[i]] = array[i];
 }
 int j = 0;
 int max1 = max + 1;
 for(i=0;i<max1;i++){
 if(temp[i] > 0){
 array[j++] = temp[i];
 }
 }
 }
}

函数 spaceToTime() 实现了数组的排序,它不计空间成本,以数组的索引下标来表示数据大小,因此避免了数字间的相互比较,这是一种典型的以空间换时间的思路。


回页首

结束语

应对、处理高吞吐量系统有很多方面可以入手,作者将以系列的方式逐步介绍覆盖所有领域。本文主要介绍了缓冲区、缓存操作、对象复用池、计算方式转换等优化及建议,从实际代码演示入手,对优化建议及方案进行了验证。作者始终坚信,没有什么优化方案是百分百有效的,需要读者根据实际情况进行选择、实践。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-27 18:51:04

高吞吐量系统设计优化建议(全文已经发表在IBM开发者论坛)的相关文章

高吞吐量系统设计优化建议

高吞吐量系统 举一个例子,我们做项目需要安排计划,每一个模块可以由多人同时并行做多项任务,也可以一个人或者多个人串行工作,但始终会有一条关键路径,这条路径就是项目的工期.系统一次调用的响应时间跟项目计划一样,也有一条关键路径,这个关键路径是就是系统影响时间.关键路径由 CPU 运算.IO.外部系统响应等等组成. 对于一个系统的用户来说,从用户点击一个按钮.链接或发出一条指令开始,到系统把结果以用户希望的形式展现出来为终止,整个过程所消耗的时间是用户对这个软件性能的直观印象,也就是我们所说的响应时

Apache kafka 工作原理介绍(作者原创文章已发表在IBM开发者论坛)

插个广告:本人的<大话Java性能优化>一书99万字,已经在亚马逊.当当.京东.天猫,感谢对技术推广梦想者的支持,谢谢! 消息队列 消息队列技术是分布式应用间交换信息的一种技术.消息队列可驻留在内存或磁盘上, 队列存储消息直到它们被应用程序读走.通过消息队列,应用程序可独立地执行--它们不需要知道彼此的位置.或在继续执行前不需要等待接收程序接收此消息.在分布式计算环境中,为了集成分布式应用,开发者需要对异构网络环境下的分布式应用提供有效的通信手段.为了管理需要共享的信息,对应用提供公共的信息交

JVM 垃圾回收器工作原理及使用实例介绍(原文已发表于IBM开发者论坛)

打个广告,本人的<大话Java性能优化>一书已经在亚马逊.当当.京东.天猫出售,感谢大家对致力于技术推广梦想者的支持. 垃圾收集基础 Java 语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况.自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担. 拥有垃圾收集器可以说是 Java 语言与 C++语言的一项显著区别.在 C++语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间.当内存释

CGroup 介绍、应用实例及原理描述(已发表于IBM开发者论坛)

原文请查看:http://www.ibm.com/developerworks/cn/linux/1506_cgroup/index.html CGroup 介绍 CGroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制.记录.隔离进程组 (process groups) 所使用的物力资源 (如 cpu memory i/o 等等) 的机制.2007 年进入 Linux 2.6.24 内核,CGroups 不是全新创造的,它将进程管理从 cpuset 中剥离

设计模式第一讲:单例模式(IBM开发者论坛已经发表的文章)

单例模式 首先我们来讲一个故事.二次世界大战的时候,我国有一个著名的战役叫"长沙保卫战",中国军队指挥官薛岳将军率领第 9 战区十余万将士,通过所谓的"焦土"战术 4 次瓦解日军的大规模进攻,给对当时的国民党政府打了一针强心剂.这四次战役中最让人我难忘的一幕是,面对单兵战斗力是中国军队 5 倍的日军,人数上虽然占据一定优势,但是只有第 10 军和第 74 军两只军队装备了现代化的军械,其余军队都是"汉阳造"的落后装备.薛将军命令第 10 军反复在

Hadoop任务优化建议 - 【Dr.Elephant系列文章-6】

使用Dr.Elephant来分析我们的任务,可以知道有哪些地方可以进行优化. 加速你的任务流程 对于特定的任务,最好有特定的参数配置.对于很多的应用场景来说,默认的任务配置并不能保证每个任务都有最好的性能.尽管对这些任务进行调优会花费一些时间,但是这些调优带来的性能提升是非常可观的. 有几个任务参数需要特别注意:mapper数量,reducer数量,io.*的配置,内存使用设置以及生成的文件数量.对这几个参数进行配置,让参数更适合当前的任务,可以极大的提升任务的执行性能. Apache的官网中H

微信红包系统设计 &amp; 优化

微信红包系统设计 & 优化 浏览次数:151次 腾讯大讲堂 2015年04月02日 字号: 大 中 小 分享到:QQ空间新浪微博腾讯微博人人网豆瓣网开心网更多0 编者按:经过2014年一年的酝酿,2015微信红包总量创下历史新高,峰值1400万次/秒,8.1亿次每分钟,微信红包收发达10.1亿次,系统整体运行平稳, 在这里我分享下微信红包背后的技术. 讲师:jeri 核心功能&目标 首先,了解下微信红包的4个逻辑:摇/发/抢/拆.看似简单,实现可不简单再review下微信红包要实现目标:

高并发系统设计 概述

通用方法 Scale Out 横向扩展,分而治之,采用分布式布署的方式分流,让每个服务器都承担一部分并发和流量 缓存 使用缓存来提高系统性能,好比"拓宽河道".任何能够降低响应时间的中间件.缓存思想遍布很多设计领域 异步 在某些场景下,未处理完成先返回,再通知. ** 高并发系统的演进应该循序渐进,以解决系统中存在的问题为目的和驱动力 ** 设计目标 高并发是运用设计手段让系统能够处理更多并发请求,这是一切架构设计的背景和前提. 高性能 性能优化原则 问题导向 二八原则 数据支撑 持续

雅虎35条性能优化建议

雅虎35条性能优化建议分7类,共35条: [内容]尽量减少HTTP请求数 [服务器]使用CDN(Content Delivery Network) [服务器]添上Expires或者Cache-Control HTTP头 [服务器]Gzip组件 [css]把样式表放在顶部 [js]把脚本放在底部 [css]避免使用CSS表达式 [js, css]把JavaScript和CSS放到外面 [内容]减少DNS查找 [js, css]压缩JavaScript和CSS [内容]避免重定向 [js]去除重复脚