反应式微服务框架Flower探讨与实践
1. 什么是Flower?
Flower既是一个反应式编程框架,又是一个分布式微服务框架。对于开发者而言,只需要针对每一个细粒度的业务功能,开发一个Service服务,并将这些Service按照业务流程进行可视化编排,即可得到一个反应式系统。
Flower的特点:
即时响应:
服务流程的调用者可以得到即时响应,无需等待整个Service流程执行完毕;Service之间无调用阻塞,即时响应。
回弹性:
当Service失效、服务器失效,系统能够进行自修复,依然保持响应,不会出现系统崩溃。
消息驱动:
Service之间通过消息驱动,完成服务流程,Service之间除了消息外没有任何调用耦合,消息流转即:前一个Service的返回值,必须是后一个Service的输入参数,Flower框架负责将前一个Service的返回值封装成一个消息,发送给后一个Service。
2.传统Web框架
由于Flower最大的特点是异步+即时,所以与之对比的是现下流行的高并发、多线程框架。
说明:
- 容器线程数限制。
对于一个高并发系统,当有很多个用户同时请求系统时,web容器将为每一个请求分配一个线程处理,而容器的线程是有限的,如果当容器的线程用完,此时还有新的用户请求到达,请求就会被阻塞或直接返回错误。 - 数据库连接限制。
当应用系统需要访问数据库时,必须获得数据库的连接,而数据库的连接数也是有限制的,且相对于用户线程数而言,更少。当数据库连接用完后,线程请求由于取不到数据库连接而被阻塞。而对于那些得到了数据库连接的线程,当数据库请求操作发送后,数据库在处理过程中,当前线程会被阻塞,无法响应其他用户请求,导致系统资源的浪费。当出现慢sql时,就会出现大量的请求堆积阻塞在数据访问层,而形成恶性循环,导致系统崩溃。
3.Flower解决传统Web的困扰
Flower相比于传统Web框架,简图如下:
优势:全异步化。
只需要有限的几个线程,就可以完成全部的用户请求操作。
- 当并发用户请求到达应用服务器,Web 容器线程将用户的 HTTP 请求变为请求对象,并将请求对象异步交给 Flower 框架的 Service 进行处理,自身则立刻返回。由于容器线程不做太多的工作,所以只需极少的容器线程就可以满足高并发的用户请求,用户的请求不会被阻塞,不会因为容器线程不足而导致阻塞。
- 用户请求交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的参数: