微服务优化之并行调用

微服务优化之并行调用

互联网产品随着用户的增加,系统对服务的高性能、高可用、可伸缩、可扩展的支持,大都采用分布式RPC框架。然而随着业务的增加,系统越来越多,系统之间的调用也越来越复杂,原本一个系统中一次请求就可以完成的工作,现在可能被分散在多个系统中,一次请求需要多个系统响应。这样就会放大RPC调用延迟带来的副作用,影响系统的高性能需求。

例如:一个RPC接口中需要依赖另外三个系统的RPC服务,各RPC服务的响应时间分别是20ms、10ms、10ms,那么这个接口的对外系统依赖的耗时40ms。如果接口依赖越多,响应时间就会越长。

对此,需要在业务范围内进行性能优化,优化思路总的来说有两种:

第一:如果对RPC接口调用,不需要关心接口的返回值,那么可以采用异步RPC调用。

第二:如果依赖RPC接口返回值,并且连续调用的多个RPC之间没有依赖关系,可以采用并行化处理。

本文主要分享一下通过并行化处理,来优化RPC接口响应时间,如上例子中的RPC采用并行调用,对外系统接口的依赖耗时会降低到20ms。

第一:因为Java对线程的使用非常方便,所以完成并行调用对于Java语言来说是相对简单,根据依赖外部接口分别创建一个线程来调用就可以完成。

第二:那么问题来:如果我的接口在完成其他接口调用后,还需要完成额外的功能而且需要依赖其他接口调用结果,该怎么处理呢?Thread类通过join调用,可以让主线程等待子线程处理结果。

第三:那么问题又来了:子线程内部的异常无法在外部获取,而需要依赖外部接口的调用结果的情况下,如果RPC接口抛出异常,必须在主线程中获取并作出相应处理,这个工作可以通过FutureTask来完成。

第四:那么问题又来了:如果一个接口依赖十个外部系统,那么每次请求就需要创建十个线程,随着接口TPS增加,系统创建线程和销毁的线程耗费的资源越来越高,这个时候需要考虑采用线程池方案了。

第五:那么问题又来了:以上实例只是一个单应用的测试Demo,真实应用情况下如上这样在代码中创建线程池并没太大意义,应该创建全局的线程池,所有请求共用线程池才能达到线程资源共用。但是Executors中线程池都默认采用AbortPolicy 的拒绝策略,在高并发情况下,就会频繁出现的线程池拒绝服务异常。此时可以考虑自定义线程池,采用CallerRunsPolicy拒绝策略,在高并发量,当线程池无法提供服务的情况下,采用主线程自己创建线程,达到并发量和计算资源的最优协调。

第六:完成以上操作就可以完美了吗?然而情况并非如此,如果细心测试发现,如果其中一个接口抛出异常时,主线程就结束了,而其他还没有执行结束的子线程将继续执行,一开始我们通过Thread.join()来协调主子线程的先后顺序,而现在采用线程池,无法在获取线程并且调用join方法,而是采用FutureTask.get()来协调先后顺序,那么还可以采用哪些方式保证主线程最后结束呢?此时可以采用一些特有的并发工具,如:闭锁,栅栏,信号量。如下为网络摘抄的三个工具对比:

闭锁(CountDownLatch)

类似于门。门初始是关闭的,试图进门的线程挂起等待开门。当负责开门进程将门打开后,所有等待线程被唤醒。

门一旦打开就不能再关闭了。

CountDownLatch(int n):指定闭锁计数器

await() :挂起等待闭锁计数器为0

countDown():闭锁计数器减1

栅栏(CyclicBarrier)

和闭锁有类似之处。闭锁是等待“开门”事件;栅栏是等待其他线程。例如有N个线程视图通过栅栏,此时先到的要等待,直到所有线程到到达后,栅栏开启,所有等待线程被唤醒通过栅栏。

CyclicBarrier(int n):需要等待的线程数量

await():挂起等待达到线程数量

信号量(Semaphore)

和锁的作用类似。区别是锁只允许被一个线程获取,但是信号量可以设置资源数量。当没有可用资源时,才被挂起等待。

Semaphore(int n):指定初始的资源数量

acquire():试图获取资源。当没有可用资源时挂起

release():释放一个资源

       本文采用栅栏完成实例代码如下:

package com.halfworlders.test.domo;

import java.util.concurrent.Callable;

import java.util.concurrent.CyclicBarrier;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.FutureTask;

import java.util.concurrent.LinkedBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

 

import com.halfworlders.test.exp.AppException;

import com.halfworlders.test.impl.ServiceImpl;

import com.halfworlders.test.intf.ServiceInterface;

 

publicclass App {

    /**

     * 外接口总数

     */

    privatestaticfinalintINTERFACE_COUNT = 10;

    ExecutorService executorService = new ThreadPoolExecutor(INTERFACE_COUNT, INTERFACE_COUNT*3, 10L,

           TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), newThreadPoolExecutor.CallerRunsPolicy());

 

    publicstaticvoid main(String[] args) {

       longstart = System.currentTimeMillis();

       App test = new App();

       test.test();

       longend = System.currentTimeMillis();

       System.out.println("总耗时:"+(end-start)+"ms");

    }

 

    @SuppressWarnings("unchecked")

    publicvoid test() {

       final CyclicBarrier cb = new CyclicBarrier(INTERFACE_COUNT + 1);

       final ServiceInterface[] services = assembles();

       final FutureTask<Integer>[] futureTasks = new FutureTask[INTERFACE_COUNT];

       for (inti = 0; i < INTERFACE_COUNT; i++) {

           final Integer fi = i;

           futureTasks[i] = new FutureTask<Integer>(new Callable<Integer>() {

              @Override

              public Integer call() throws Exception {

                  try {

                     returnservices[fi].service();

                  } finally {

                     cb.await();

                  }

              }

           });

           executorService.submit(futureTasks[i]);

       }

       String serviceName = null;

       try {

           // 打开栅栏

           cb.await();

           // 如果有其他系统调用异常,则将该异常向外层抛出

           for (inti = 0; i < INTERFACE_COUNT; i++) {

              serviceName = services[i].getName();

              futureTasks[i].get();

           }

       } catch (Exception e) {

           if ((einstanceof ExecutionException) && (e.getCause() instanceof AppException)) {

              throw (AppException) e.getCause();

           } else {

              thrownew RuntimeException(serviceName+"系统异常", e);

           }

       }

    }

   

    private ServiceInterface[] assembles(){

       ServiceInterface[] service = new ServiceInterface[INTERFACE_COUNT];

       for (inti = 0; i < INTERFACE_COUNT; i++) {

           service[i] = new ServiceImpl("接口"+i);

       }

        returnservice;

    }

}

 

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值