大量並發的應急處理方案與實踐1——非同步處理
往往我們會遇到這樣的問題,以前一直運行良好的系統由於突然遇到大量並發訪問而崩潰,這時你的客戶和老闆全都急上了房。情況緊急重新改造系統架構非常困難需要時間。這時你非常後悔,如果當時採用分散式架構那麼現在只要水平增加應用或數據伺服器
就好了,所有現存數據和應用也不會受到任何影響。關於系統架構可參考我的另一篇文章:《開餐館與做軟體——如何提高大型網站性能》
http://www.rosoo.net/a/201008/10059.html
現在我們只好採用一些應急的解決方案。 舉例來說,一個房間只能容納500人,現在突然來了1000人這時我們該怎麼辦?
辦法有兩個:一是增加房間面積,這個辦法如同水平增加應用或數據伺服器,需要對架構進行重新設計,因此將涉及很多問題,或許並不比重新造省時間。另一個辦法就是讓客人排隊,當屋裡有人出去,再讓別人進來,即非同步處理。
非同步處理,當資源不足的情況下對已經到來的訪問請求並不馬上處理而是讓這些請求排隊等候,直到有可用的資源再進行處理。這種辦法的代價就是時間,不能及時返回處理結果。但是當我們沒有條件改造房屋的時候這是唯一的辦法。
現在有個問題,我們讓客人在哪裡排隊,大廳(硬碟)還是門口(內存)。答案似乎很明顯,哪裡離房間近最好就在哪裡等,但是,速度快的地方往往空間不富裕。將用戶請求以某種方式先保存在硬碟上是一種比較常用的方法,當然也因此多了一步將數據載入到內存地過程。
在內存中將數據排隊的方法,可使用數組,哈希表,鏈表等數據結構,也可使用消息隊列等現成的組件如ActiveMQ 。如我們使用一個單例模式的Map
對象,保存來自多個並發的請求。
import java.util.Map;
public class TestMap {
private volatile static TestMap
singleton=null;
private static Map testmap =
null;
private TestMap(){}
public static TestMap getInstance()
{
if(singleton==null){
synchronized(TestMap.class)
{
singleton=new TestMap();
}
}
return singleton;
}
public Map getTestmap() {
return testmap;
}
public void setTestmap(Map testmap) {
TestMap.testmap =
testmap;
}
}
在硬碟中排隊,就是將數據直接寫到硬碟里,例如在Java 中可將對象直接保存到硬碟中,代碼如下:
public static boolean writeObject(String filePath, Object
entity)
{
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(filePath);
} catch
(FileNotFoundException e) {
// TODO Auto-generated catch
block
e.printStackTrace();
return
false;
}
ObjectOutputStream oos;
try
{
oos = new ObjectOutputStream(fos);
oos.writeObject(entity);
oos.close();
} catch
(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return true;
}
public static Object readObject(String filePath)
{
Object
entity = null;
FileInputStream fis = null;
try
{
fis = new FileInputStream(filePath);
} catch
(FileNotFoundException e) {
// TODO Auto-generated catch
block
e.printStackTrace();
}
ObjectInputStream ois;
try {
ois = new
ObjectInputStream(fis);
try {
entity =
ois.readObject();
} catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
ois.close();
}
catch (IOException e) {
// TODO Auto-generated catch
block
e.printStackTrace();
}
return
entity;
}
最後我們需要一個監控模塊(如一個線程)進行調度, 例如:
public class testThread implements Runnable{
private static long interval = 3000; //循環間隔
@Override
public void run(){
while(true)
{
... //這裡是需要執行的代碼
try {
Thread.sleep(interval);
} catch
(InterruptedException e) {
// TODO Auto-generated catch
block
e.printStackTrace();
}
}
}
}