很多开发人员说,将应用程序切换到异步处理很复杂。因为他们有一个天然需要同步通信的Web应用程序。在这篇文章中,我想介绍一种方法来达到异步通信的目的:使用一些众所周知的库和工具来设计他们的系统。 下面的例子是用Java编写的,但我相信它更多的是基本原理,同一个应用程序可以用任何语言来重新写。

所需的工具和库:

  • Spring Boot
  • RabbitMQ

1.Web应用程序

一个用Spring MVC编写的Web应用程序并运行在Tomcat上。 它所做的只是将一个字符串发送到一个队列中 (异步通信的开始) 并等待另一个队列中的消息作为HTTP响应发送回来。

首先,我们需要定义几个依赖项,然后等待Spring Boot执行所有必要的自动配置。

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-amqp</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>com.thedeanda</groupId>
  12. <artifactId>lorem</artifactId>
  13. </dependency>
  14. </dependencies>
  1. @SpringBootApplication
  2. public class BlockingApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(BlockingApplication.class, args);
  5. }
  6. @RestController
  7. public static class MessageController {
  8. private final RabbitTemplate rabbitTemplate;
  9. public MessageController(CachingConnectionFactory connectionFactory) {
  10. this.rabbitTemplate = new RabbitTemplate(connectionFactory);
  11. }
  12. @GetMapping("invoke")
  13. public String sendMessage() {
  14. Message response = rabbitTemplate.sendAndReceive("uppercase", null, request());
  15. return new String(response.getBody());
  16. }
  17. private static Message request() {
  18. Lorem LOREM = LoremIpsum.getInstance();
  19. String name = LOREM.getFirstName() + " " + LOREM.getLastName();
  20. return new Message(name.getBytes(), new MessageProperties());
  21. }
  22. }
  23. @Bean
  24. public CachingConnectionFactory connectionFactory() {
  25. CachingConnectionFactory factory = new CachingConnectionFactory();
  26. factory.setAddresses("localhost:5672");
  27. factory.setUsername("admin");
  28. factory.setPassword("admin");
  29. return factory;
  30. }
  31. }

2.消费端应用程序

第二个应用程序仅仅是一个等待消息的RabbitMQ的消费端,将拿到的字符串转换为大写,然后将此结果发送到输出队列中。

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-amqp</artifactId>
  5. </dependency>
  6. </dependencies>
  1. @SpringBootApplication
  2. public class ServiceApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(ServiceApplication.class, args);
  5. }
  6. public static class MessageListener {
  7. public String handleMessage(byte[] message) {
  8. Random rand = new Random();
  9. // Obtain a number between [0 - 49] + 50 = [50 - 99]
  10. int n = rand.nextInt(50) + 50;
  11. String content = new String(message);
  12. try {
  13. Thread.sleep(n);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. return content.toUpperCase();
  18. }
  19. }
  20. @Bean
  21. public CachingConnectionFactory connectionFactory() {
  22. CachingConnectionFactory factory = new CachingConnectionFactory();
  23. factory.setAddresses("localhost:5672");
  24. factory.setUsername("admin");
  25. factory.setPassword("admin");
  26. return factory;
  27. }
  28. @Bean
  29. public SimpleMessageListenerContainer serviceListenerContainer() {
  30. SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
  31. container.setConnectionFactory(connectionFactory());
  32. container.setConcurrentConsumers(20);
  33. container.setMaxConcurrentConsumers(40);
  34. container.setQueueNames("uppercase_messages");
  35. container.setMessageListener(new MessageListenerAdapter(new MessageListener()));
  36. return container;
  37. }
  38. }

3.底层如何执行的?

程序启动并首次调用sendMessage()方法后,我们可以看到Spring AMQP支持自动创建了一个新的回复队列并等待来自我们的服务应用程序的响应。

  1. 2019-05-12 17:23:21.451 INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : Container initialized for queues: [amq.rabbitmq.reply-to]
  2. 2019-05-12 17:23:21.457 INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : SimpleConsumer [queue=amq.rabbitmq.reply-to, consumerTag=amq.ctag-VF-iqD9rLEuljIBstbCI1A identity=10e58093] started

如果我们在消费端应用程序中查看消息,我们可以看到Spring自动传播有关回复队列的信息以及**相关ID,**用于将其传递回Web应用程序以便能够将请求和响应配对在一起。

这就是发生魔术的地方。 当然,如果您想使其更复杂,您可以在协作中包含更多服务,然后将Web应用程序的最终响应放入与自动生成的队列不同的队列中, 该队列只具有正确的关联ID。 另外,不要忘记设置合理的超时。

这个解决方案还有一个很大的缺点 - 应用程序吞吐量。 我故意这样做,以便我可以跟进这篇文章,进一步深入调查AsyncProfiler! 但是目前,我们使用Tomcat作为主HTTP服务器,默认为200个线程,这意味着我们的应用程序无法同时处理200多条消息,因为我们的服务器线程正在等待RabbitMQ 回复队列的响应,直到有消息进入或发生超时。

感谢您阅读本文,敬请关注后续内容! 如果您想自己尝试一下,请查看我的GitHub存储库

原文链接:dzone.com/articles/ho…

作者:Petr Bouda

译者:KeepGoingPawn

很多开发人员说,将应用程序切换到异步处理很复杂。因为他们有一个天然需要同步通信的Web应用程序。在这篇文章中,我想介绍一种方法来达到异步通信的目的:使用一些众所周知的库和工具来设计他们的系统。 下面的例子是用Java编写的,但我相信它更多的是基本原理,同一个应用程序可以用任何语言来重新写。
所需的工具和库:
- Spring Boot- RabbitMQ
## 1.Web应用程序
一个用Spring MVC编写的Web应用程序并运行在Tomcat上。 它所做的只是将一个字符串发送到一个队列中 (异步通信的开始) 并等待另一个队列中的消息作为HTTP响应发送回来。
首先,我们需要定义几个依赖项,然后等待Spring Boot执行所有必要的自动配置。
```java<dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-amqp</artifactId>    </dependency>    <dependency>        <groupId>com.thedeanda</groupId>        <artifactId>lorem</artifactId>    </dependency></dependencies>```
```java@SpringBootApplicationpublic class BlockingApplication {    public static void main(String[] args) {        SpringApplication.run(BlockingApplication.class, args);    }    @RestController    public static class MessageController {        private final RabbitTemplate rabbitTemplate;        public MessageController(CachingConnectionFactory connectionFactory) {            this.rabbitTemplate = new RabbitTemplate(connectionFactory);        }        @GetMapping("invoke")        public String sendMessage() {            Message response = rabbitTemplate.sendAndReceive("uppercase", null, request());            return new String(response.getBody());        }        private static Message request() {            Lorem LOREM = LoremIpsum.getInstance();            String name = LOREM.getFirstName() + " " + LOREM.getLastName();            return new Message(name.getBytes(), new MessageProperties());        }    }    @Bean    public CachingConnectionFactory connectionFactory() {        CachingConnectionFactory factory = new CachingConnectionFactory();        factory.setAddresses("localhost:5672");        factory.setUsername("admin");        factory.setPassword("admin");        return factory;    }}```

## 2.消费端应用程序
第二个应用程序仅仅是一个等待消息的RabbitMQ的消费端,将拿到的字符串转换为大写,然后将此结果发送到输出队列中。

```java<dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-amqp</artifactId>    </dependency></dependencies>```

```java@SpringBootApplicationpublic class ServiceApplication {    public static void main(String[] args) {        SpringApplication.run(ServiceApplication.class, args);    }    public static class MessageListener {        public String handleMessage(byte[] message) {            Random rand = new Random();            // Obtain a number between [0 - 49] + 50 = [50 - 99]            int n = rand.nextInt(50) + 50;            String content = new String(message);            try {                Thread.sleep(n);            } catch (InterruptedException e) {                e.printStackTrace();            }            return content.toUpperCase();        }    }    @Bean    public CachingConnectionFactory connectionFactory() {        CachingConnectionFactory factory = new CachingConnectionFactory();        factory.setAddresses("localhost:5672");        factory.setUsername("admin");        factory.setPassword("admin");        return factory;    }    @Bean    public SimpleMessageListenerContainer serviceListenerContainer() {        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();        container.setConnectionFactory(connectionFactory());        container.setConcurrentConsumers(20);        container.setMaxConcurrentConsumers(40);        container.setQueueNames("uppercase_messages");        container.setMessageListener(new MessageListenerAdapter(new MessageListener()));        return container;    }}```

### 3.底层如何执行的?
程序启动并首次调用sendMessage()方法后,我们可以看到Spring AMQP支持自动创建了一个新的**回复队列**并等待来自我们的服务应用程序的响应。

```2019-05-12 17:23:21.451  INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : Container initialized for queues: [amq.rabbitmq.reply-to]2019-05-12 17:23:21.457  INFO 4574 --- [nio-8080-exec-1] .l.DirectReplyToMessageListenerContainer : SimpleConsumer [queue=amq.rabbitmq.reply-to, consumerTag=amq.ctag-VF-iqD9rLEuljIBstbCI1A identity=10e58093] started```

如果我们在消费端应用程序中查看消息,我们可以看到Spring自动传播有关**回复队列**的信息以及**相关ID,**用于将其传递回Web应用程序以便能够将请求和响应配对在一起。
这就是发生魔术的地方。 当然,如果您想使其更复杂,您可以在协作中包含更多服务,然后将Web应用程序的最终响应放入与自动生成的队列不同的队列中, 该队列只具有正确的*关联ID*。 另外,不要忘记设置合理的超时。
这个解决方案还有一个很大的缺点 - 应用程序吞吐量。 我故意这样做,以便我可以跟进这篇文章,进一步深入调查`AsyncProfiler`! 但是目前,我们使用Tomcat作为主HTTP服务器,默认为200个线程,这意味着我们的应用程序无法同时处理200多条消息,因为我们的服务器线程正在等待RabbitMQ **回复队列**的响应,直到有消息进入或发生超时。
感谢您阅读本文,敬请关注后续内容! 如果您想自己尝试一下,请查看我的[GitHub存储库](https://github.com/petrbouda/rabbitmq-async-microservices)。
> 原文链接:https://dzone.com/articles/how-to-split-up-a-synchronous-and-asynchronous-of
> [作者:Petr Bouda](https://github.com/petrbouda)
> [译者:KeepGoingPawn](https://github.com/KeepGoingPawn)

Java系统中如何拆分同步和异步的更多相关文章

  1. 【Java面试题】25 同步和异步有何异同,在什么情况下分别使用他们?举例说明。

    如果数据将在线程间共享.例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取. 当应用程序在对象上调用了一个需要花费很长时间 ...

  2. Java对多线程~~~Fork/Join同步和异步帧

    于Fork/Join骨架,当提交的任务,有两个同步和异步模式.它已被用于invokeAll()该方法是同步的.是任何 务提交后,这种方法不会返回直到全部的任务都处理完了.而还有还有一种方式,就是使用f ...

  3. C#中委托的同步和异步有什么区别

    通过定义委托,来实现同步和异步,委托通过Action和Func来实现,记录学习委托的同步和异步的过程 委托的同步方法 委托的Invoke方法用来进行同步调用. static void Main(str ...

  4. 关于js中Ajax的同步、异步使用

    下面一个简单的例子,说明前后端交互中,Ajax同步和异步的使用 1.设置简单的一个div,包含触发事件 CompanyType() <div> <input type="h ...

  5. java 线程之对象的同步和异步

    一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.th ...

  6. 面试刷题11:java系统中io的分类有哪些?

    随着分布式技术的普及和海量数据的增长,io的能力越来越重要,java提供的io模块提供了足够的扩展性来适应. 我是李福春,我在准备面试,今天的问题是: java中的io有哪几种? java中的io分3 ...

  7. 什么是java的线程安全?同步,异步

    线程是比进程更小的执行单位,是在进程基础上进行的进一步划分.所谓多线程是指进程在执行过程中可以产生多个同时存在.同时运行的线程.多进程机制可以合理利用资源,提高程序的运行效率.一个进程至少包含一个线程 ...

  8. iOS开发中如何实现同步、异步、GET、POST等请求实操演示!

    1.同步请求可以从因特网请求数据,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作, 2.异步请求不会阻塞主线程,而会建立一个新的线程来操作,用户发出异步请求后,依然 ...

  9. JS中Ajax的同步和异步

    ajax同步 : 意味着此时请求Server后,JS代码不再继续执行,等待Server返回后才继续往下执行. ajax异步 : 意味着此时请求Server后,JS代码继续执行,不管Server什么时候 ...

随机推荐

  1. cf 512D - Fox And Travelling

    题目大意 给定一颗\(n\le 100\)个点的图,可以进行随机游走,求游走\(k=0...n\)个点的方案数 游走的规则是:每次只能访问一个度数\(\le 1\)的点,并将其删除 分析 看完傻眼 问 ...

  2. 【Educational Codeforces Round 53 (Rated for Div. 2)】

    A:https://www.cnblogs.com/myx12345/p/9853775.html B:https://www.cnblogs.com/myx12345/p/9853779.html ...

  3. Java中的IO基本用法

    先贴一下我在作业中用到的三种文件输入辅助类.三种文件输出辅助类 public class BuffIn implements InHelp{ private BufferedReader buffer ...

  4. JSTL获取Session的ID与获取文件的真实路径与项目名称

    今天在测试集群配置的时候想到session共享,因此想要获取sessionID,可以通过下面方法: ${pageContext.session.id} 获取文件的真实路径: <%=request ...

  5. java 修改字体大小

    在Windows->Preferences->General->Appearance->Colors and Fonts->Java->Java Editor Te ...

  6. 8大排序算法的java实现--做个人收藏

    排序算法分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因为数据量太大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.这里只讨论内部排序,常见的内部排序算法有:插入排序 ...

  7. Python那些事

    Python这几年很火,在这里我用问答的方式来总结一下使用python的一些常见问题,对自己是个总结,也希望对有同样问题的朋友有帮助.   Q:Python为什么流行? A:Python是一个比较方便 ...

  8. concurrencyProgrammingGuide 1

    thread用来表述执行代码的独立path.os x的线程执行基于POSIX 线程API. process用来表述一个运行操作,可以包含多个线程. task用来描述工作的抽象概念. Concurren ...

  9. 对象第复制operator=

    类机制中有默认的对象复制操作符=,自定义对象复制需要注意一个问题,如果有遇到指针指向的资源是需要释放的,这时需要毫不留情释放,否则内存空间的泄露就不可避免.复制操作与拷贝构造函数的参数是一致的,只是在 ...

  10. 使用virtualenv, uwsgi, nginx来布署django

    http://blog.csdn.net/lihao21/article/details/47731903 http://www.cnblogs.com/lxg226/p/3468558.html h ...