反应式微服务框架Flower探讨与实践

反应式微服务框架Flower探讨与实践

1. 什么是Flower?

Flower既是一个反应式编程框架,又是一个分布式微服务框架。对于开发者而言,只需要针对每一个细粒度的业务功能,开发一个Service服务,并将这些Service按照业务流程进行可视化编排,即可得到一个反应式系统。
Flower的特点:

即时响应:

服务流程的调用者可以得到即时响应,无需等待整个Service流程执行完毕;Service之间无调用阻塞,即时响应。

回弹性:

当Service失效、服务器失效,系统能够进行自修复,依然保持响应,不会出现系统崩溃。

消息驱动:

Service之间通过消息驱动,完成服务流程,Service之间除了消息外没有任何调用耦合,消息流转即:前一个Service的返回值,必须是后一个Service的输入参数,Flower框架负责将前一个Service的返回值封装成一个消息,发送给后一个Service。

2.传统Web框架

由于Flower最大的特点是异步+即时,所以与之对比的是现下流行的高并发、多线程框架。
在这里插入图片描述
说明:

  1. 容器线程数限制。
    对于一个高并发系统,当有很多个用户同时请求系统时,web容器将为每一个请求分配一个线程处理,而容器的线程是有限的,如果当容器的线程用完,此时还有新的用户请求到达,请求就会被阻塞或直接返回错误。
  2. 数据库连接限制。
    当应用系统需要访问数据库时,必须获得数据库的连接,而数据库的连接数也是有限制的,且相对于用户线程数而言,更少。当数据库连接用完后,线程请求由于取不到数据库连接而被阻塞。而对于那些得到了数据库连接的线程,当数据库请求操作发送后,数据库在处理过程中,当前线程会被阻塞,无法响应其他用户请求,导致系统资源的浪费。当出现慢sql时,就会出现大量的请求堆积阻塞在数据访问层,而形成恶性循环,导致系统崩溃。

3.Flower解决传统Web的困扰

Flower相比于传统Web框架,简图如下:
在这里插入图片描述
优势:全异步化。
只需要有限的几个线程,就可以完成全部的用户请求操作。

  1. 当并发用户请求到达应用服务器,Web 容器线程将用户的 HTTP 请求变为请求对象,并将请求对象异步交给 Flower 框架的 Service 进行处理,自身则立刻返回。由于容器线程不做太多的工作,所以只需极少的容器线程就可以满足高并发的用户请求,用户的请求不会被阻塞,不会因为容器线程不足而导致阻塞。
  2. 用户请求交Service 对象后,Service 之间依然是使用异步消息通讯的方式进行调用,不会直接进行阻塞式的调用。当 Service 完成业务逻辑处理后,会返回一个处理结果,这个结果也会以消息的方式异步发送给它的下一个Service。Service 之间使用了 AKKA Actor 进行消息通信,调用者的 Service 发送调用消息后,不需要等待被调用者返回结果,就可以处理自己的下一个消息。

使用 Flower 开发的系统,在一个典型的 Web 应用中,几乎没有任何地方会被阻塞,所有的线程都可以被不断地复用,有限的线程就可以完成大量的并发用户请求,从而大大地提高了系统的吞吐能力和响应时间,同时,由于线程不会被阻塞,应用就不会因为并发量太大或者数据库处理缓慢而宕机,从而提高了系统的可用性

4.Flower代码实操

maven包:

底层是Akka,所以需要Akka相关包:

com.typesafe.akka
akka-actor_2.13


com.typesafe.akka
akka-http_2.13


com.typesafe.akka
akka-remote_2.13


com.typesafe.akka
akka-slf4j_2.13


com.typesafe.akka
akka-stream_2.13

目前大都使用的是艺龙在Akka基础上封装的Flower,这次我们简单引用Flower-common做代码演示。

com.ly.train
flower.common
0.1

Service简单应用

1. 编写简单Service逻辑,然后进行编排调用。

Service1:

public class Service1 implements Service<Message1> {
  @Override
  public Object process(Message1 message) {
    System.out.println("Service1: " +
            "接收参数:name:"+message.getM2().getName()+" age:"+message.getM2().getAge());
    return message.getM2();
  }
}

Service2:

public class Service2 implements Service<Message2> {
  @Override
  public Object process(Message2 message) {
    int age = message.getAge() + 1;
    System.out.println("Service2: age add 1: "+age);
    return age;
  }
}

Service3:

public class Service3 implements Service<Message2> {
  @Override
  public Object process(Message2 message) {
    System.out.println("Service3: name UpperCase: "+message.getName().toUpperCase());
    return message.getName().toUpperCase();
  }
}

Service4:

public class Service4 implements Service<Set> {
  @Override
  public Object process(Set message) {
    Message2 m = new Message2();
    for (Object o : message) {
      if (o instanceof Integer) {
        m.setAge((Integer) o);
      }
      if (o instanceof String) {
        m.setName(String.valueOf(o));
      }
    }
    Message3 m3 = new Message3();
    m3.setM2(m);
    System.out.println("Service4: 其他逻辑:"
            + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    return m3;
  }
``
调用和编排启动类:
```java
public class Sample {
  public static void main(String[] args) throws Exception {
    EnvBuilder.buildEnv(Sample.class);
    Message2 m2 = new Message2(30, "wangfu");
    Message1 m1 = new Message1();
    m1.setM2(m2);
    System.out.println(ServiceFacade
            .syncCallService("sample", "service1", m1));
  }
}

执行结果:
在这里插入图片描述

buildFlow 编排

下面示例中是根据消息编排,即Message1~Message3代码如下:

public class Message1 implements FirstMessage {
  private Message2 m2;
  public Message2 getM2() {
    return m2;
  }
  public void setM2(Message2 m2) {
    this.m2 = m2;
  }
}
public class Message2  {
  private String name;
  private int age;
  private int id;
  public Message2() {
  }
  public Message2(int age, String name) {
    this.age = age;
    this.name = name;
  }
  //省getter setter
}
public class Message3 implements ReturnMessage {
  private Message2 m2;
  public Message2 getM2() {
    return m2;
  }
  public void setM2(Message2 m2) {
    this.m2 = m2;
  }
  public String toString() {
    return "结果: " + m2.getName() + " " + m2.getAge();
  }
}

可视化流程编排

public static void buildEnv(Class clz) throws Exception {
    Predicate<String> filter = new FilterBuilder().include(".*\\.services").include(".*\\.flow");

    Reflections reflections = new Reflections(new ConfigurationBuilder()
            .filterInputsBy(filter)
            .setScanners(new ResourcesScanner())
            .setUrls(ClasspathHelper.forClass(clz)));

    Set<String> servicesFiles = reflections.getResources(Pattern.compile(".*\\.services"));
    for (String path : servicesFiles) {
      ServiceFactory.registerService(FileUtil.readService("/" + path));
    }
    Set<String> flowFiles = reflections.getResources(Pattern.compile(".*\\.flow"));
    for (String path : flowFiles) {
      String flowName = path.substring(0, path.lastIndexOf("."));
      ServiceFlow.buildFlow(flowName, FileUtil.readFlow("/" + path));
    }
  }

然后在resource下编写.flow和.services文件即可,如下:
在这里插入图片描述
.flow

service1 -> service2
service1 -> service3
service2 -> service4
service3 -> service4

.services

service1 = com.xxxx.Service1
service2 = com.xxxx.Service2
service3 = com.xxxx.Service3
service4 = com.xxxx.Service4

2.http请求调用

启动类:

public class WebServer {
  public static void main(String[] args) throws Exception {
    Server server = new Server(8080);
    ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
    context.setContextPath("/");
    server.setHandler(context);
    context.addServlet(new ServletHolder(new FlowServlet()), "/test-flow");
    server.start();
    server.join();
  }
}

servlet类:

public class FlowServlet extends HttpServlet {
    ServiceRouter sr;
    ActorRef actor;
    @Override
    public void init() {
        buildServiceEnv();
        sr = ServiceFacade.buildServiceRouter("flow", "flowService", 400);
        actor = ActorSystem.create("sample").actorOf(Props.create(RouterActor.class));
    }
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("servlet doGet ");
        out.flush();
        AsyncContext ctx = req.startAsync();

        // flower
        asyncExe(ctx);

    }
    private void asyncExe(AsyncContext ctx) {
        try {
            sr.asyncCallService(" Hello, Flow World! ", ctx);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private void buildServiceEnv() {
        ServiceFactory.registerService("flowService",
                "com.xxxx.FlowService");
    }
}

FlowService

public class FlowService implements HttpService, Complete, Flush {
  @Override
  public Object process(Object message, Web web) throws Exception {
    web.println(" -> FlowService process");
    System.out.println("接收到的参数:"+message);
    return "";
  }
}

测试:访问启动类中的 /test-flow,经过servlet,到达 FlowService。
在这里插入图片描述
同时控制台显示Servlet 传给 FlowService的参数:
在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值