分布式系统的思想就是:
如果一个系统的压力过大,可以把一个服务拆分成多个服务,这个叫垂直拆分。
也可以考虑做镜像集群,负载平衡,这个叫水平拆分。
这个系统我们可以考虑垂直拆分,将订单相关的功能拆分出来。
我们将订单的逻辑拿出来,放到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";
}