微服务设计原则——高性能:异步与并发

2024-08-19 14:03:20 浏览数 (3)

1.异步

对于处理耗时长的任务,如果采用同步等待的方式,会严重降低系统的吞吐量,可以采用异步化进行解决。

异步(Asynchronous)是一种编程模型或执行方式,在这种方式中,任务的启动和完成不是同步的,也就是说,程序不会在等待任务完成时阻塞,而是可以继续执行其他操作。当异步任务完成时,程序会通过回调、通知或事件的方式获取结果或处理后续操作。

1.1 调用异步

异步调用发生在使用异步编程模型来提高代码效率的时候,实现方式主要有:

  • Callback

异步回调通过注册一个回调函数,然后发起异步任务,当任务执行完毕时会回调用户注册的回调函数,从而减少调用端等待时间。这种方式会造成代码分散难以维护,定位问题也相对困难。

  • Promise 和 Future

当用户提交一个任务时会立刻先返回一个Future,然后任务异步执行,后续可以通过 Future 获取执行结果。

JavaScript 中的 Promise 和 Java 中的 CompletableFuture 都是常见的异步处理方式。

代码语言:javascript复制
// 使用 Promise 处理异步操作
let promise = new Promise((resolve, reject) => {
  // 模拟异步操作
  setTimeout(() => resolve('Operation complete'), 1000);
});

promise.then(result => console.log(result));  // 输出: "Operation complete"
  • Async/Await

Async/Await 是对 Promise 的进一步封装,提供了更简洁、更易读的异步代码写法。使用 async 标记的函数会返回一个 Promise,而 await 关键字用于暂停函数的执行,直到 Promise 处理完成。

JavaScript 和 Python 都支持 async/await 语法。

代码语言:javascript复制
async function fetchData() {
  let response = await fetch('https://api.example.com/data');
  let data = await response.json();
  console.log(data);
}

fetchData();
  • 事件驱动

事件驱动模型常用于 GUI 编程和服务器端编程,通过事件触发异步操作的处理。

比如 Node.js 通过事件驱动模型来处理异步 I/O 操作。

代码语言:javascript复制
const EventEmitter = require('events');
const eventEmitter = new EventEmitter();

// 定义事件处理程序
eventEmitter.on('dataReceived', (data) => {
  console.log('Data received:', data);
});

// 模拟异步数据接收
setTimeout(() => eventEmitter.emit('dataReceived', 'Sample Data'), 1000);
  • CPS(Continuation-Passing Style)

可以对多个异步编程进行编排,组成更复杂的异步处理,并以同步的代码调用形式实现异步效果。

CPS 将后续的处理逻辑当作参数传递给 Then 并可以最终捕获异常,解决了异步回调代码散乱和异常跟踪难的问题。Java 中的 CompletableFuture 和 C PPL(Parallel Patterns Library)基本支持这一特性。典型的调用形式如下:

代码语言:javascript复制
void handleRequest(const Request &req) {
  return req.Read().Then([](Buffer &inbuf){
      return handleData(inbuf);
  }).Then([](Buffer &outbuf){
      return handleWrite(outbuf);
  }).Finally(){
      return cleanUp();
  });
}

关于 CPS 更多信息推荐阅读:2018 中国 C 大会的吴锐_C 服务器开发实践部分。

调用异步是一种异步编程模型,通过允许任务非阻塞地执行,使程序在等待某些操作(如 I/O 操作)完成的同时,能够继续处理其他任务,从而提高并发性和性能。常见的实现方式包括回调函数、Promise/Future、Async/Await,以及事件驱动模型等。

1.2 流程异步

一个业务流程往往伴随着调用链路长、后置依赖多等特点,导致业务流程处理耗时长,降低了系统的可用性和并发处理能力。

同步改异步,可以降低主链路的处理耗时。

举个例子,比如我们去 KFC 点餐,遇到排队的人很多,当点完餐后,大多情况下我们会隔几分钟就去问好了没,反复去问了好几次才拿到,在这期间我们也没法干活了。

这个就叫同步轮训,这样效率太低了。

服务员被问烦了,就在点完餐后给我们一个号码牌,每次准备好了就会在服务台叫号,这样我们就可以在被叫到的时候再去取餐,中途可以继续干自己的事。这就叫异步。

当一个微服务需要处理大量的请求或任务时,直接处理这些任务可能导致服务压力过大。通过消息队列(如 Kafka、RabbitMQ、RocketMQ 等)对耗时的任务进行异步化。

微服务将任务消息发送到消息队列中,另一个处理服务从队列中取出任务异步处理。这样可以解耦服务,提升系统的弹性和扩展性。

1.3 数据流异步

处理大量实时数据流(如日志数据、传感器数据)。

使用流处理框架(如 Apache Flink、Spark Streaming)来异步处理数据流。数据被实时处理和分析,结果可以用于实时监控和决策。

1.4 小结

在微服务架构中,异步处理可以显著提高系统的性能和扩展性。通过异步编程模型、消息队列、数据流异步处理,微服务可以更高效地处理并发请求、管理任务和优化资源利用。异步编程不仅提升了系统的响应速度,还增强了系统的灵活性和可扩展性。

2.并发

在微服务架构中,并发处理是提升系统性能和响应能力的关键技术。并发使得系统能够同时处理多个任务或请求,显著提高吞吐量和资源利用率。

2.1 请求并发

如果一个任务需要处理多个子任务,可以将没有依赖关系的子任务并发化,这种场景在后台开发很常见。

如一个请求需要查询 3 个数据,分别耗时 T1、T2、T3,如果串行调用总耗时 T=T1 T2 T3。对三个任务执行并发,总耗时 T=max(T1,T 2,T3)。同理,写操作也如此。对于同种请求,还可以同时进行批量合并,减少 RPC 次数。

2.2 冗余请求

冗余请求指同时向后端服务发送多个同样的请求,谁响应快就是使用谁,其他的则丢弃。

这种策略缩短了主调方的等待时间,但也使整个系统调用量猛增,一般适用于初始化或者请求少的场景。比如腾讯的移动连通服务维纳斯(WNS,Wireless Network Service)的跑马模块其实就是这种机制,跑马模块为了快速建立长连接同时向后台多个 IP/Port 发起请求,谁快就用谁,这在弱网的移动设备上特别有用,如果使用等待超时再重试的机制,无疑将大大增加用户的等待时间。

这种方式较少使用,知道即可。

2.3 小结

并发在微服务架构中通过多线程/多进程、异步 I/O、并行计算和流处理等方式,显著提升了系统的处理能力和性能。它能够有效地处理大量并发请求和数据流,优化资源利用率,并提高系统的响应速度和吞吐量。通过合理应用并发技术,微服务系统可以更好地应对高负载和高并发的挑战。

参考文献

服务高并发、高性能、高可用实现方案- 杨岂

1 人点赞