分佈式系統的思想就是:
如果一個系統的壓力過大,可以把一個服務拆分成多個服務,這個叫垂直拆分。
也可以考慮做鏡像集羣,負載平衡,這個叫水平拆分。
這個系統我們可以考慮垂直拆分,將訂單相關的功能拆分出來。
我們將訂單的邏輯拿出來,放到order-service中,通過backend來調用order-service
來創建訂單。
服務:backend 接受客戶端請求,判斷userid是否訂購過(redis判斷userid存在即訂購過),判斷是否有庫存。
order-service 提供接口,供backend調用,創建訂單,減庫存。
有個問題:當我有1000個併發,同時提交到backend,並且通過了判斷用戶,判斷庫存,
這個時候就會有1000個網絡請求到order-service,網絡消耗很大,很多併發量大的項目都有這類問題
這個時候,我們可以考慮,是否可以在order-service弄個批量創建訂單的接口。
在消費端backend,可以做個隊列,存儲需要調用的數據,
每10MS去把隊列的數據讀出來組成一個批量訂單數據,調用order-service的批量接口,
將返回的數據,對應的之前提交的每個request的task-future,這樣就可以減少服務與服務之間的交互。\
其實很多架構和中間件都有這種方案,有個設置 batchsize或者是limittime等,都是設置當你隊列大小爲多少個爲一批
或者是多少時間段內一批,都是爲了減少建立連接消耗和網絡消耗
思路大概是這樣,代碼傳至git上。快速地址,核心代碼如下:
@Component
@Slf4j
public class OrderService {
/**
* 請求包類
*/
class Request{
String seckillId;
String userid;
//每個請求一個線程觀察者
CompletableFuture<String> future;
}
//存請求的隊列
LinkedBlockingDeque<Request> queue = new LinkedBlockingDeque<>();
public String createOrder(String seckillId,String userid) throws ExecutionException, InterruptedException {
Request request = new Request();
request.seckillId=seckillId;
request.userid=userid;
CompletableFuture<String> future = new CompletableFuture<>();
request.future=future;
//加入到請求隊列
queue.add(request);
//返回等待,
return future.get();
}
@Autowired
private RemoteOrderService remoteOrderService;
/**
* bean初始化的時候,啓動一個線程
* 每10MS,把隊列裏的請求批量請求
*/
@PostConstruct
public void init(){
ScheduledExecutorService scheduledExecutorService=
Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
int size = queue.size();
if(size ==0){
return;
}
//隊列裏面存在數據
JSONArray jsonArray = new JSONArray();
ArrayList<Request> requests = new ArrayList<>();
JSONObject jsonObject;
Request request;
//取出隊列,組成批量數據
for(int i=0;i<size;i++){
jsonObject=new JSONObject();
request = queue.poll();
requests.add(request);
jsonObject.put("seckillId",request.seckillId);
jsonObject.put("userid",request.userid);
jsonArray.add(jsonObject);
}
log.info("批量提交的大小:"+jsonArray.size());
//調用遠程創建訂單的批量接口
JSONObject result = remoteOrderService.createOrder(jsonArray);
//正確返回
if(result.getString("returnCode").equals("100")){
for (Request request1 : requests) {
JSONObject jsonObject1 = result.getJSONObject("returnData");
//通過userid唯一取出結果並將請求的future完成觸發之前的請求等待
String result1 = jsonObject1.getString(request1.userid);
request1.future.complete(result1);
}
}
}
},0,10,TimeUnit.MICROSECONDS);//定時任務每10毫秒調用一次
}
}
結果:我們可以看到,本來需要服務間調用500次,現在通過隊列批量只調用了6次,大大減少了網絡消耗。
我感覺這波操作很6,在大併發的情況下,大大的減少了通信消耗。是不是?
其他的判斷用戶在redis是否購買過,減庫存等代碼
//判斷之前userid是否購買
if(redisTemplate.opsForValue().getAndSet(userid,1)!=null){
return "0";
}
redisTemplate.opsForValue().set(userid,"1",1000L*60,TimeUnit.MICROSECONDS);
/**
* 從redis獲取庫存數,因爲redis的單線程,所以也不會出現超賣現象
*/
long num = redisTemplate.opsForValue().decrement(seckillId);
if(num<0){
//庫存已售完
return "0";
}