异步框架

异步是一种程序设计的思想,使用异步模式设计的程序可以显著减少线程等待,从而在高吞吐量的场景中,极大提升系统的整体性能,显著降低时延。异步思想就是,当我们要执行一项比较耗时的操作时,不去等待操作结束,而是给这个操作一个命令:“当操作完成后,接下来去执行什么。”

使用异步编程模型,虽然并不能加快程序本身的速度,但可以减少或者避免线程等待,只用很少的线程就可以达到超高的吞吐能力。
只有类似在像消息队列这种业务逻辑简单并且需要超高吞吐量的场景下,或者必须长时间等待资源的地方,才考虑使用异步模型。

异步是用来提高cup的利用率,而不是节省线程。
异步编程是通过分工的方式,是为了减少了cpu因线程等待的可能,让CPU一直处于工作状态。换句话说,如果我们能想办法减少CPU空闲时间,我们的计算机就可以支持更多的线程。

【1】异步框架: CompletableFuture (concurrent包中的,并发框架)
Java8 内置的CompletableFuture和 ReactiveX 的RxJava,

/**
 * 账户服务
 */
public interface AccountService {
    /**
     * 变更账户金额
     * @param account 账户 ID
     * @param amount 增加的金额,负值为减少
     */
    CompletableFuture add(int account, int amount);
}

/**
 * 转账服务
 */
public interface TransferService {
    /**
     * 异步转账服务
     * @param fromAccount 转出账户
     * @param toAccount 转入账户
     * @param amount 转账金额,单位分
     */
    CompletableFuture transfer(int fromAccount, int toAccount, int amount);
}


/**
 * 转账服务的实现
 */
public class TransferServiceImpl implements TransferService {
    @Inject
    private  AccountService accountService; // 使用依赖注入获取账户服务的实例
    @Override
    public CompletableFuture transfer(int fromAccount, int toAccount, int amount) {
      // 异步调用 add 方法从 fromAccount 扣减相应金额
      return accountService.add(fromAccount, -1 * amount)
      // 然后调用 add 方法给 toAccount 增加相应金额
      .thenCompose(v -> accountService.add(toAccount, amount));    
    }
}

//客户端调用
public class Client {
    @Inject
    private TransferService transferService; // 使用依赖注入获取转账服务的实例
    private final static int A = 1000;
    private final static int B = 1001;

    public void syncInvoke() throws ExecutionException, InterruptedException {
        // 同步调用
        transferService.transfer(A, B, 100).get();
        System.out.println(" 转账完成!");
    }

    public void asyncInvoke() {
        // 异步调用
        transferService.transfer(A, B, 100)
                .thenRun(() -> System.out.println(" 转账完成!"));
    }
}


在异步实现中,回调方法 OnComplete() 是在什么线程中运行的?我们是否能控制回调方法的执行线程数?该如何做?
CompletableFuture默认是在ForkjoinPool commonpool里执行的,也可以指定一个Executor线程池执行,借鉴guava的ListenableFuture的时间,回调可以指定线程池执行,这样就能控制这个线程池的线程数目了。
CompletableFuture有点需要注意的是,在不同的业务中需创建各自的线程池,否则都是共用ForkJoinPool。

【2】异步网络框架-netty
1、发送数据
同步,因为只要发送时的缓存不满,或者发送数据的速度没有超过网卡的网速,耗时就是内存写入耗时
2、读取数据
netty,事先定义好收到数据后的处理逻辑,把这个逻辑作为回调方法,在连接建立前就通过框架提供的API设置好,当收到数据的时候,就由框架自动执行这个回调方法

// 创建一组线程
EventLoopGroup group = new NioEventLoopGroup();

try{
    // 初始化 Server
    ServerBootstrap serverBootstrap = new ServerBootstrap();
    serverBootstrap.group(group);
    serverBootstrap.channel(NioServerSocketChannel.class);
    //在本地的9999端口启动服务,接受数据
    serverBootstrap.localAddress(new InetSocketAddress("localhost", 9999));

    // 设置收到数据后的处理的 Handler
    serverBootstrap.childHandler(new ChannelInitializer() {
        protected void initChannel(SocketChannel socketChannel) throws Exception {
        																//需要继承ChannelInboundHandlerAdapter
            socketChannel.pipeline().addLast(new MyHandler());
        }
    });
    // 绑定端口,开始提供服务
    ChannelFuture channelFuture = serverBootstrap.bind().sync();
    channelFuture.channel().closeFuture().sync();
} catch(Exception e){
    e.printStackTrace();
} finally {
    group.shutdownGracefully().sync();
}

服务启动后,如果有客户端来请求连接,netty就会自动的创建一个socket连接,当收到客户端的数据后,netty就会在EventLoopGroup对象中获取一个io线程,在这个io线程中调用接受数据的回调方法,来执行接收数据的业务逻辑

netty自己维护一组线程来执行数据收发的逻辑,如果需要更灵活的实现,可以选择NIO
Netty 自动地解决了线程控制、缓存管理、连接管理这些问题,用户只需要实现对应的 Handler 来处理收到的数据即可。
【3】NIO(NIO包)

而 NIO 是更加底层的 API,它提供了 Selector 机制,用单个线程同时管理多个连接,解决了多路复用这个异步网络通信的核心问题。

你可能感兴趣的:(基础增强,异步)