写点什么

案例分析 -- 反应式编程框架 Flower 的设计

用户头像
张荣召
关注
发布于: 2020 年 09 月 27 日





程序运行: 普通场景

  1. 请求到达:用户请求服务器,服务器使用Web端口(比如:80)服务用户请求。

  2. 1请求/1线程:Web容器(比如Tomcat)为每个用户请求启动一个线程,由这个线程同步处理全部请求。

  3. 数据库访问:再数据库连接池中获取数据库连接,访问数据库。

  4. 访问数据库物理文件(磁盘IO)

程序同步阻塞:

 -1. 数据未到达而被阻塞:用户请求使用网络连接到服务器Web端口,由于网络原因,后者客户端原因,数据发送比较慢,

       还没有到达服务器Web端口,线程从网络连接中读取数据,读取不到数据,就会等待数据而被阻塞。

0. 线程同步访问被阻塞:通过网络同步访问第三方服务,同步访问数据库而被阻塞。  

  1. 等待数据库连接而被阻塞:数据库连接数量可能小于线程数量,多个线程同时争抢数据库连接,没有抢到数据库连接的线程就会被阻塞。

  2. 数据库请求排队阻塞:数据库连接发送请求通过网络连接到达数据库,多个请求需要排队

  3. 访问文件系统(磁盘IO操作)而被阻塞: 访问数据库的文件系统,因IO操作而被阻塞 

 程序崩溃:高并发,高负载场景

  1. 高并发请求同时到达:大量用户请求同时到达服务器。

  2. Web容器创建大量线程:线程数量急剧上升。创建线程是个昂贵操作,需要耗费服务器资源(CPU,内存),到达一定数量,耗尽服务器资源,无法再创建更多的线程

  3. 后续请求等待: 后续到达的用户请求需要等待,超时后,仍然无法处理,从用户请求的角度看,服务器不可用。

  4. Web容器内线程争夺资源被阻塞:Web容器内大量线程为了完成服务,争抢资源,资源不足时,线程因为等待其他资源而被阻塞。线程不释放自己的资源,导致后来的线程无资源可用也会被阻塞====>整个系统响应变慢。

  5. 硬件资源不足导致系统越来越慢:CPU,内存,带宽不足时,系统就会越来越慢。

  6. 大量的用户请求无法得到处理,系统就崩溃啦。

分析崩溃原因:

     核心原因:阻塞。 阻塞导致线程不能释放自己的资源,其他线程无资源可用,自己因被阻塞而无法向下运行。后来的请求,无法被处理,最终导致系统崩溃。简言之:阻塞导致线程争抢资源,系统资源被耗尽。

解决方法:解除阻塞----异步。

解决方案:反应式编程框架----Flower。



异步关注点:如何改善系统性能?如何实现异步的?编程如何实现的?编程框架如何设计的?



解析:

1.请求并发到达服务器,访问Web端口(80端口),每个请求建立Socket连接,这些连接等待Web容器处理请求。

2.Web容器使用有限的几个线程(容器线程),甚至一个线程,处理所有的用户请求连接---多路复用

   ===>多路socket的请求使用一个或者几个有限的线程,遍历多个socket网络连接,为他们进行网络通信操作。

   有限的线程可以复用更多的并发请求。

3.容器线程从socket连接中抽取request和response对象,然后将request和response对象交给后面的应用程序处理。

   socket的连接中如果没有数据,就没有容器线程处理这个connection。===>没有线程阻塞。

   socket的connection连接中如果有数据,容量线程将数据抽取为request和response对象,交给后面的应用程序。

   ==>有限的容器线程,就可以处理很多个并发连接===>避免无限制创建线程,导致系统资源耗尽。

4.Flower运行环境有自己的线程。提供自己的线程池。容器线程提交request和response数据到Flower后,

   Flower将数据交给Service处理。线程再某个service上执行。

5.ServiceA处理完后,调用ServiceB。

  传统方式--同步调用:ServiceA同步调用ServiceB,ServiceB进行IO操作被阻塞,ServiceA也会被阻塞,不能往下执行。ServiceA和ServiceB都被阻塞。

  Flower异步调用:ServiceA处理完成后,发送消息给ServiceB。ServiceB使用akka线程执行自己,

                           拿到ServiceA传递过来的消息,进行处理。完成后,发送消息给ServiceC。

  Flower中并发执行:ServiceA,ServiceB,ServiceC并发执行。ServiceA处理完A用户request的第一步后,发送消息给ServiceB,ServiceB执行的时候;ServiceA开始处理B用户的request。

                               ===>ServiceA和ServiceB使用不同的线程同时处理。

6.异步数据库访问:

         传统方式:获取数据库连接,getConnection(),如果没有可用数据库连接,就会被阻塞。

         Flower方式:异步数据库连接驱动,返回的是Future,知道Future完成操作Complete,

                            由另外一个线程拿到Connection提交到数据库==>获取数据库连接的过程是不阻塞的。



效果解析:因为线程少,不会造成很多的资源消耗;因为不阻塞,提高系统的并发量

                ==>整体的性能很好,系统的吞吐量TPS和响应时间表现都很好。



Flower实现异步的基础是Akka的Actor:

原理解析:生产者和消费者模式,使用消息队列,分离生产者和消费者。===>解决直接调用的阻塞问题。

                Service被封装在Actor中,Actor之间相互通信。

  1. Sender发送消息给一个Actor。

       同步调用:Sender 直接调用Actor,Actor自己被阻塞,Sender也会被阻塞。

       异步调用:使用消息机制。Sender不直接调用Actor,而是发送消息Message给Actor。

    2.消息发送:Actor在自己门牌号前挂了一个邮箱(队列),发送过来的消息,先入队放入MailBox邮箱。

    3.消息分派:Dispatcher分发器定时分派分发邮箱中的消息给Actor

    4.Actor处理消息

    5.处理完成后,封装消息发送到下一个Actor。



Akka示例:

    public class Hello extends UntypedActor{

          public void onReceive(Object message){

                 System.out.println("Hello,world");

          } 

   }

  

   ActorRef hello=...;

   hello.tell("hi!");



Service开发示例:

      //1.开发异步编程Service

       public class ServiceA implements Service<String,String> {

             public String process(String message,ServiceContext context){

                    if(message!=null && message intanceof String){

                         return ((String)message).trim();

                    }

                    return "";

             }

       }

      //2.编排Service流程,serviceA->serviceB->serviceC

        ServiceFlow.getOrCreate(flowName,serviceFactory)

                          .buildFlow(ServiceA.class,ServiceB.class)

                          .buildFlow(ServiceB.class,ServiceC.class);



     //3.调用异步服务:

        flowerFactory.getServiceFacade().asyncCallService(flowName,"hello world");



兼容Spring的Flower Web开发:

@RestController

@RequestMapping("/order/")

@Flower(value="createOrderFlow",flowNumber=32)

public class CreateOrderController extends FlowerController{

     @RequestMapping(value="createOrder") 

     public void createOrder(OrderDTO orderDTO,HttpServletRequest request)throws exception{

          doProcess(orderDTO);

     }



     protected void doProcess(Object param,HttpServletRequest request)throws IOException{

         AsyncContext context=null;

         if(request!=null){ context=req.startAsync();}

         flowRouter.asncCallService(param,context);

     } 

}



Flower 异步访问数据库:

@FlowerService

public class UserService implements Service<User,CompletableFuture<Serializable>>{

       @Autowired

       private UserDTO userDTO;

       

       @Override

       public CompletableFuture<Serializable> process(User message,ServiceContext context)throws throwable{

            System.out.println("处理用户信息:"+message);

            return userDTO.insert(message);

       }

}

Flower 核心模块设计:

     



用户头像

张荣召

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
案例分析--反应式编程框架Flower的设计