使用Powertools for Amazon Lambda简化Amazon AppSync Events集成

实时功能已成为现代应用程序的必需品,用户期望即时更新和交互式体验。无论你是在构建聊天应用程序、实时仪表板、游戏排行榜还是IoT系统,Aamzon AppSync Events通过WebSocket API实现这些实时功能,让你能够构建可扩展且高性能的实时应用程序,而无需担心规模或连接管理。

Powertools for Aamzon Lambda是一个开发者工具包,包括可观测性、批处理、Aamzon Systems Manager Parameter Store集成、幂等性、功能标志、Amazon CloudWatch Metrics、结构化日志记录等功能。Powertools for Aamzon现在通过新的AppSyncEventsResolver支持AppSync Events,可在Python、TypeScript和.NET中使用。这个新功能通过专为帮助你专注于业务逻辑而设计的功能增强了开发体验。AppSyncEventsResolver为处理事件提供了简单一致的接口,内置支持过滤、转换和路由事件等常见模式。

在这篇文章中,你将看到TypeScript的示例,但你可以使用Powertools for Aamzon (Python)Powertools for Aamzon (.NET)在Python和.NET函数中使用相同的功能。

图1 使用Aamzon AppSync、Lambda和Powertools的实时事件处理架构

在这篇文章中,你将学习如何:

  • 使用AppSyncEventsResolver设置事件处理器
  • 实现不同的事件处理模式以获得最佳性能
  • 使用基于模式的路由来组织你的事件处理器
  • 利用内置功能处理常见用例

入门指南

AppSyncEventsResolver为在你的Aamzon Lambda函数中处理AppSync Events提供了简单的声明式方法。事件解析器允许你监听PUBLISHSUBSCRIBE事件。PUBLISH事件在客户端向频道发送消息时发生,而SUBSCRIBE事件在客户端尝试订阅频道时发生。你可以为不同的命名空间和频道注册处理器来管理你的事件驱动通信。

让我们探索如何开始使用以及将增强你开发体验的核心功能。以下是如何设置AppSyncEventsResolver的基本示例:

typescript 复制代码
import {
  AppSyncEventsResolver,
  UnauthorizedException,
} from '@aws-lambda-powertools/event-handler/appsync-events';

// 消息处理的类型定义
type ChatMessage = {
    userId: string;
    content: string;
}

// 简单的授权检查
const isAuthorized = (path: string, userId?: string): boolean => {
    // 根据你的授权系统检查
    if (path.startsWith('/chat/private') && !userId) {
        return false;
    }
    return true;
};

// 消息处理逻辑
const processMessage = async (payload: ChatMessage) => {
    // - 验证消息内容
    // - 存储到数据库
    // - 用额外数据丰富
    return {
        ...payload,
        timestamp: new Date().toISOString()
    };
};

const app = new AppSyncEventsResolver();

// 处理特定频道的发布事件
app.onPublish('/chat/general', async (payload: ChatMessage) => {
    // 处理并返回消息
    return processMessage(payload);
});

// 处理所有频道的订阅事件
app.onSubscribe('/*', async (info) => {
    const {
        channel: { path },
        request,
    } = info;

    // 执行访问控制检查
    if (!isAuthorized(path, userId)) {
        throw new UnauthorizedException(`不允许订阅 ${path}`);
    }

    return true;
});

export const handler = async (event, context) =>
  app.resolve(event, context);

AppSyncEventsResolver类负责解析传入的事件数据,并根据事件类型调用适当的处理器方法。让我们分解一下发生了什么:

基于模式的路由

AppSyncEventsResolver使用直观的基于模式的路由系统,允许你根据频道路径组织事件处理器。你可以:

  • 处理特定频道 (/chat/general)
  • 为命名空间使用通配符 (/chat/*)
  • 创建全局捕获所有处理器 (/*)
typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

// 特定频道处理器
app.onPublish('/notifications/alerts', async (payload) => {
    // 你的逻辑在这里
});

// 处理notifications命名空间中的所有频道
app.onPublish('/notifications/*', async (payload) => {
    // 你的逻辑在这里
});

// 未处理频道的全局捕获所有
app.onPublish('/*', async (payload) => {
    // 你的逻辑在这里
});

export const handler = async (event, context) =>
  app.resolve(event, context);

最通用的捕获所有处理器是/*,它将匹配任何命名空间和频道,而/default/*将匹配default命名空间中的任何频道。当多个处理器匹配同一事件时,库将调用最具体的处理器并忽略不太具体的处理器。例如,如果为/default/channel1注册了一个处理器,为/default/*注册了另一个处理器,Powertools将为匹配/default/channel1的事件调用第一个处理器并忽略第二个。这为你提供了对事件处理方式的控制,并避免不必要的处理。如果事件不匹配任何处理器,默认情况下Powertools将按原样返回事件并记录警告。 这意味着事件将在不进行任何修改的情况下传递。这种方法有助于逐步采用库,允许你使用自定义逻辑处理特定事件,而其他事件由默认行为处理。

订阅处理

Powertools还提供了处理订阅事件的简单方法。它将自动解析传入事件并根据事件类型调用适当的处理器。 默认情况下,AppSync允许订阅,除非你的Lambda处理器抛出错误或明确拒绝请求。当订阅被拒绝时,AppSync将向客户端返回4xx响应并阻止建立订阅。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';
import type { Context } from 'aws-lambda';

const metrics = new Metrics({
  namespace: 'serverlessAirline',
  serviceName: 'chat',
  singleMetric: true,
});
const app = new AppSyncEventsResolver();

app.onSubscribe('/default/foo', (event) => {
  metrics.addDimension('channel', event.info.channel.path);
  metrics.addMetric('connections', MetricUnit.Count, 1);
});

export const handler = async (event: unknown, context: Context) =>
  app.resolve(event, context);

当订阅事件到达时,库调用适当的处理器并将事件对象作为第一个参数传递。你可以根据订阅事件采取任何必要的操作,例如运行访问控制检查:

typescript 复制代码
app.onSubscribe('/private/*', async (info) => {
  const userGroups =
    info.identity?.groups && Array.isArray(info.identity?.groups)
      ? info.identity?.groups
      : [];
  const channelGroup = 'premium-users';

  if (!userGroups.includes(channelGroup)) {
    throw new UnauthorizedException(
      `订阅需要 ${channelGroup} 组成员身份`
    );
  }
})

订阅事件遵循相同的匹配规则,并提供对完整事件和上下文的相同访问。你可以通过使用通配符*字符为任何命名空间或频道注册捕获所有处理器,还可以在处理器中访问完整的事件和上下文对象。

访问完整事件和上下文

虽然解析器简化了事件处理,但在需要时你仍然可以完全访问事件和上下文对象。 这在你需要额外信息的场景中很有用,例如请求头或Lambda上下文中的剩余执行时间,以实现自定义逻辑。

解析器将完整的事件和上下文作为第二个和第三个参数传递给每个处理器。这让你可以访问所有相关信息而无需更改现有代码。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Logger } from '@aws-lambda-powertools/logger';

const logger = new Logger({
  logLeveL: 'INFO',
  serviceName: 'serverlessAirline'
});
const app = new AppSyncEventsResolver({ logger});

app.onPublish('/orders/process', async (payload, event, context) => {
    // 访问请求头
    const { headers } = event.request;
    
    // 访问Lambda上下文
    const { getRemainingTimeInMillis } = context;

    logger.info('处理事件详情', {
        headers,
        remainingTime: getRemainingTimeInMillis()
    });

    return payload;
});

export const handler = async (event, context) =>
  app.resolve(event, context);

错误处理

AppSyncEventsResolver提供内置错误处理,防止Lambda函数失败,同时确保错误正确传达给AppSync,然后AppSync将其传播给客户端。 当处理器中发生错误时,解析器不会使整个Lambda调用失败,而是捕获错误并将其包含在失败的特定事件的响应负载中。

这种方法确保Lambda函数继续执行,同时为AppSync提供正确格式的错误消息。在处理多个事件时,如果一个事件失败,其他事件继续正常处理。 这在并行处理场景中特别有用,你希望确保一个事件中的错误不会影响其他事件的处理。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

app.onPublish('/messages', async (payload) => {
    // 如果消息包含"error",抛出异常
    if (payload.message === "error") {
        throw new Error("无效消息");
    }
    return payload;
});

export const handler = async (event, context) =>
  app.resolve(event, context);

// 处理此事件时:
// {
//     "id": "123",
//     "payload": {
//         "message": "error"
//     }
// }

// 解析器将返回:
// {
//     "id": "123",
//     "error": "Error - 无效消息"
// }

高级模式和最佳实践

AppSyncEventsResolver具有额外的高级功能,帮助你构建健壮且可维护的实时应用程序。让我们探索这些功能以及如何有效使用它们。

发布处理

默认情况下,我们为每条消息调用一次你的路由处理器。这让你专注于业务逻辑并避免编写样板代码,而Powertools处理消息迭代并转换事件和响应格式。 你只需要返回想要用作负载的值,或为该消息抛出错误。然后库将负载与正确的事件ID关联。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';
import { Metrics, MetricUnit } from '@aws-lambda-powertools/metrics';

type SensorReading = {
    deviceId: string;
    temperature: number;
    humidity: number;
    timestamp: string;
}

const app = new AppSyncEventsResolver();
const metrics = new Metrics({ namespace: 'SensorReadings' });

app.onPublish('/sensors/readings', async (payload: SensorReading) => {
    // 独立处理每个传感器读数
    if (payload.temperature > 100) {
        metrics.addDimension('alertType', 'highTemperature');
        metrics.addMetric('HighTemperature', MetricUnit.Count, 1);
        throw new Error('温度读数过高');
    }

    // 用处理时间戳丰富负载
    return {
        ...payload,
        processed: true,
        processedAt: new Date().toISOString()
    };
});

export const handler = async (event, context) =>
  app.resolve(event, context);

这种模式通过让你只编写单个事件的逻辑来简化开发,Powertools自动处理其余部分。

聚合处理

聚合模式让你将多个事件作为单个批次处理,而不是单独处理它们。 这在你想要优化资源使用时特别有用,例如在单个操作中向数据库发送多个查询,或在处理之前一起分析多个事件。 虽然两种模式都为你提供对事件处理的完全控制,但聚合模式提供对整个事件列表的一次性访问。

要实现这一点,你可以将aggregate选项设置为true。使用此模式时,解析器在单个调用中将整个事件列表发送给你的处理器,让你将它们作为批次处理。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

const app = new AppSyncEventsResolver();

app.onPublish('/default/*', async (events) => {
  const results = [];
  for (const event of events) {
    try {
      results.push(await handleDefaultNamespaceCatchAll(event));
    } catch (error) {
      results.push({
        error: {
          errorType: 'Error',
          message: error.message,
        },
        id: event.id,
      });
    }
  }

  return results;
}, {
  aggregate: true,
});

export const handler = async (event, context) =>
  app.resolve(event, context);

注意aggregate选项仅适用于发布事件,当使用此选项时,你负责处理事件并返回适当的响应。 Powertools仍将负责将事件路由到正确的处理器,但你对事件的处理方式拥有完全控制权。

事件过滤

要过滤掉事件,在频道处理器中抛出错误。如果处理器为特定事件抛出错误,库会捕获它并在相同索引处向响应列表添加错误对象。这表明相应的消息应该被丢弃。 这允许你静默过滤事件或向订阅者提供有意义的错误反馈。

typescript 复制代码
import { AppSyncEventsResolver } from '@aws-lambda-powertools/event-handler/appsync-events';

app.onPublish('/moderation/*', async (payload) => {
    // 过滤掉不当内容
    if (await containsInappropriateContent(payload)) {
        throw new CustomError('内容违反指导原则');
    }

    // 处理有效内容
    return await processContent(payload);
});

export const handler = async (event, context) =>
  app.resolve(event, context);

结论

Powertools for Aamzon中的AppSyncEventsResolver通过为处理实时事件提供简单一致的接口来增强你使用AppSync Events的开发体验。 通过减少样板代码并为常见模式提供内置支持,你可以专注于业务逻辑而不是基础设施代码。

了解更多:

我们很兴奋看到你用这些新功能构建什么。分享你的反馈,让我们知道你如何在应用程序中使用AppSyncEventsResolver

最后提醒一下各位工友,如果后续不再使用相关服务,别忘了在控制台关闭,避免超出免费额度产生费用~

相关推荐
神秘的猪头6 小时前
弹性布局vsinline-block
前端
王六岁6 小时前
# 🐍 前端开发 0 基础学Python小结 Python数据类型使用场景与用途指南
前端·python
平生不晚6 小时前
优化使用img标签加载svg大图导致的内存开销
前端·浏览器
Zyx20076 小时前
弹性布局:告别“挤来挤去”的CSS布局时代——深入理解 Flexbox
前端·css
Apifox6 小时前
Apifox 10 月更新|支持实时预览在线文档个性化配置的效果、性能优化、测试能力升级
前端·后端·测试
玉宇夕落6 小时前
🔥 一行代码让网页“活”起来!前端黑科技 Stylus + Flex 实战,小白也能写出酷炫交互动画!
前端·javascript
feiyu_gao6 小时前
如何将一个大表格的数据转为图片
前端·性能优化
Mintopia6 小时前
🌌 AIGC与AR/VR结合:Web端沉浸式内容生成的技术难点
前端·javascript·aigc
拖拉斯旋风6 小时前
前端学习之弹性布局(上):弹性布局的基本知识
前端