使用函数计算构建图片处理应用

本文涉及的产品
函数计算FC,每月15万CU 3个月
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
简介:

这篇文章介绍了如何利用阿里云函数计算的多种独特功能来构建图片处理 Web 服务。

介绍函数计算

函数计算 是阿里云的 Serverless 计算平台,通过函数计算,工程师只需编写几行代码,即可开发互联网规模化服务。它可以无缝地进行资源管理、弹性伸缩和负载均衡,使开发人员可以专注于业务逻辑的开发,而不必费心管理底层基础架构,从而轻松构建可快速响应新信息的应用程序。在内部,我们利用容器技术并开发专有分布式算法,依托弹性扩展的资源编排用户的代码。我们已在内部开发了许多尖端技术,以求为我们的用户提供高可扩展性、可靠性和优异性能的服务。

在本指南中,我们将向您介绍展示其创新功能的分步式教程。如果这是您首次使用函数计算平台,您可以阅读此 快速入门指南 以熟悉基本的 Serverless 概念。

使用网络文件系统

我们推出的 访问 NAS 的功能,支持函数访问阿里云 文件存储服务

益处

该平台的 Serverless 性质意味着每次调用时,用户代码可以在不同的实例上运行。这也意味着,函数不能依赖其本地文件系统来存储任何中间结果。开发人员必须依赖其他云服务,如 对象存储服务,在各函数或调用之间共享处理的结果。这不是理想做法,因为引入其他分布式服务将带来额外的开发成本,并增加代码的复杂性。

为了攻克此难题,我们开发了“访问文件存储 (NAS)”功能。NAS 是另一项阿里云服务,它提供高度可扩展、可靠且可用的分布式文件系统,并支持 标准文件访问协议。我们可以将远程 NAS 文件系统挂载到用户代码借以运行的资源上,从而有效地为函数代码创建一个“本地”文件系统。

图片爬虫示例

此演示部分介绍了如何创建 Serverless Web 爬虫程序,可以从种子网页下载所有图片。在 Serverless 平台上运行该程序是一个挑战,因为它不能在给定时间限制下,通过一个函数获取所有网站信息。但是,通过使用 NAS 功能,这一问题便迎刃而解,用户可以使用 NAS 文件系统在各次函数运行之间共享数据。下面我们介绍分步式教程。 我们假定您已理解 VPC 的概念,并知道如何在 VPC 中创建 NAS 挂载点。 如果您不了解这些内容,则可在继续以下步骤之前阅读 基础 NAS 教程

使用 NAS 配置创建服务

1.登录 函数计算控制台
2.选择您的 NAS 所在的目标地域。
3.创建使用预创建的 NAS 文件系统的服务。在本演示中:
Screen Shot 2018-09-11 at 11.35.38 PM.png
1.输入服务名称描述
2.启用高级设置
3.填写VPC 配置字段,确保您选择的 VPC 是 NAS 挂载点所在的 VPC。
Screen Shot 2018-09-11 at 11.37.28 PM.png
完成VPC 配置后,会显示NAS 配置字段。

  1. 按如下示例,填写NAS 配置字段:
    NAS Config.jpg

    1.UserIdGroupId字段是函数在其下运行的 uid/gid。它们决定了在 NAS 文件系统上创建的所有文件的所有者。在此次演示中,您可以选择任何用户/设备组名,它们将在本服务中的所有函数间共享。
    2.NAS 挂载点下拉菜单列出了可从所选 VPC 访问的所有有效 NAS 挂载点。
    3.远程路径是 NAS 文件系统的一个目录,它不需要是 NAS 文件系统的根目录。请选择用于存储图片的目录。
    4.本地挂载路径是函数可以访问远程目录的本地目录,请记住您在此处选择的内容。
    5.使用 日志服务 为您所需的日志库目标配置日志。

    1. 确保您配置了 角色 以授权函数计算平台访问 VPC 和日志库。
      7.点击确定

创建一个每五分钟启动一次的函数

我们已经创建了带有 NAS 访问的服务,现在可以编写爬虫程序了。 由于爬虫程序函数必须运行多次才能完成,因此我们使用 时间触发器 每 5 分钟调用一次。
1.登录到函数计算控制台,并选择您刚刚创建的服务
2.通过单击加号,为服务创建 函数
Create Function.jpg
3.函数计算平台提供了各种函数模板,可帮助您快速构建应用程序。选择为此演示创建一个空函数,然后单击“下一步”。但是如果您有时间,也可以试用其他模板。
4.在下一页的下拉菜单中选择时间触发器。填写触发器名称,将调用间隔设置为 5 分钟,将事件留空,然后单击“下一步”。
Time Trigger.jpg
5.填写函数名称,并确保选择 java8 runtime。然后填写函数处理程序,将内存设置为 2048MB,将“超时时间”设置为 300 秒,然后单击“下一步”
java function.jpg
6.单击“下一步”并检查预览,然后单击“创建”。

在 Java 中写入爬虫程序

现在,您应该会看到函数代码页,可以写入爬虫程序了。处理程序逻辑非常简单,如下所示。

  • 解析时间触发器事件以获取爬网程序配置。
  • 根据配置创建图片爬网程序。爬网程序使用 JAVA HTML 解析器 解析 HTML 页,以识别图片和链接。
  • 读取 NAS 文件系统中已被和尚未被访问的 Web 页面列表(仅当此函数在新环境中运行时)。
  • 继续对 Web 页面的深度优先遍历,并使用爬虫程序下载这一过程中发现的任何新图片。
  • 将新找到的 Web 页面保存到 NAS 文件系统。
    下面是 Java 代码的摘录,您可以看到,我们以与本地文件系统相同的方式向 NAS 文件系统读取和写入文件。

    public class ImageCrawlerHandler implements PojoRequestHandler<TimedCrawlerConfig, CrawlingResult> {
         
      private String nextUrl() {
         
          String nextUrl;
          do {
         
              nextUrl = pagesToVisit.isEmpty() ? "" : pagesToVisit.remove(0);
          } while (pagesVisited.contains(nextUrl) );
          return nextUrl;
      }
    
      private void initializePages(String rootDir) throws IOException {
         
          if (this.rootDir.equalsIgnoreCase(rootDir)) {
         
              return;
          }
          try {
         
              new BufferedReader(new FileReader(rootDir + CRAWL_HISTORY)).lines()
                  .forEach(l -> pagesVisited.add(l));
              new BufferedReader(new FileReader(rootDir + CRAWL_WORKITEM)).lines()
                  .forEach(l -> pagesToVisit.add(l));
          } catch (FileNotFoundException e) {
         
              logger.info(e.toString());
          }
          this.rootDir = rootDir;
      }
    
      private void saveHistory(String rootDir, String justVistedPage, HashSet<String> newPages)
          throws IOException {
         
          //将爬网历史记录附加到文件末尾
          try (PrintWriter pvfw = new PrintWriter(
              new BufferedWriter(new FileWriter(rootDir + CRAWL_HISTORY, true)));
          ) {
         
              pvfw.println(justVistedPage);
          }
          //将等待爬网的工作条目附加到文件末尾
          try (PrintWriter ptfw = new PrintWriter(
              new BufferedWriter(new FileWriter(rootDir + CRAWL_WORKITEM, true)));
          ) {
         
              newPages.stream().forEach(p -> ptfw.println(p));
          }
      }
    
      @Override
      public CrawlingResult handleRequest(TimedCrawlerConfig timedCrawlerConfig, Context context) {
         
          CrawlingResult crawlingResult = new CrawlingResult();
          this.logger = context.getLogger();
          CrawlerConfig crawlerConfig = null;
          try {
         
              crawlerConfig = JSON_MAPPER.readerFor(CrawlerConfig.class)
                  .readValue(timedCrawlerConfig.payload);
          } catch (IOException e) {
         
              ....
          }
          ImageCrawler crawler = new ImageCrawler(
              crawlerConfig.rootDir, crawlerConfig.cutoffSize, crawlerConfig.debug, logger);
          int pagesCrawled = 0;
          try {
         
              initializePages(crawlerConfig.rootDir);
              if (pagesToVisit.isEmpty()) {
         
                  pagesToVisit.add(crawlerConfig.url);
              }
              while (pagesCrawled < crawlerConfig.numberOfPages) {
         
                  String currentUrl = nextUrl();
                  if (currentUrl.isEmpty()) {
         
                      break;
                  }
                  HashSet<String> newPages = crawler.crawl(currentUrl);
                  newPages.stream().forEach(p -> {
         
                      if (!pagesVisited.contains(p)) {
         
                          pagesToVisit.addAll(newPages);
                      }
                  });
                  pagesCrawled++;
                  pagesVisited.add(currentUrl);
                  saveHistory(crawlerConfig.rootDir, currentUrl, newPages);
              }
              //计算图片的总大小
             .....
          } catch (Exception e) {
         
              crawlingResult.errorStack = e.toString();
          }
    
          crawlingResult.totalCrawlCount = pagesVisited.size();
          return crawlingResult;
      }
    }
    
    AI 代码解读
    public class ImageCrawler {
         
    ...
    public HashSet<String> crawl(String url) {
         
          links.clear();
          try {
         
              Connection connection = Jsoup.connect(url).userAgent(USER_AGENT);
              Document htmlDocument = connection.get();
              Elements media = htmlDocument.select("[src]");
              for (Element src : media) {
         
                  if (src.tagName().equals("img")) {
         
                      downloadImage(src.attr("abs:src"));
                  }
              }
              Elements linksOnPage = htmlDocument.select("a[href]");
              for (Element link : linksOnPage) {
         
                  logDebug("Plan to crawl `" + link.absUrl("href") + "`");
                  this.links.add(link.absUrl("href"));
              }
    
          } catch (IOException ioe) {
         
             ...
          }
          return links;
      }
    }
    
    AI 代码解读

    为了简单起见,我们省略了一些细节和其他帮助类程序。如果您想要运行该代码,从您喜爱的网站上抓取图片,那么您可以从 awesome-fc github project 中获取完整代码。

运行爬虫程序

现在,代码已经编写好,可以运行了。步骤如下。

  • 我们使用 maven 来执行依赖关系和版本管理。在与 Maven 库同步后(假定您已安装 Maven),只需键入以下命令,即可创建要上传的 jar 文件。
mvn clean package
AI 代码解读
  • 在函数页面中选择“代码”选项卡。通过控制台上传在上一步中创建的 jar 文件(以依赖项的名称结尾)。
    java function.jpg
  • 在函数页面中选择“触发器”选项卡。单击时间触发器链接,以 Json 格式输入事件。Json 事件将被序列化到爬网程序配置并传递到函数。单击“确定”。
    timer event.jpg
  • 时间触发器每五分钟调用一次爬虫程序功能。 每次调用时,处理程序将拾取仍需要访问的 URL 列表,并从第一个开始。
  • 您可以选择“日志”选项卡来搜索爬虫程序执行日志。

创建 Serverless 服务

我们推出的第二项 功能 允许任何人发送 HTTP 请求,以直接触发函数执行。

益处

现在,我们已经有了装有从 Web 下载的图片的文件系统,我们希望找到一种方式,来通过 Web 服务提供这些图片。传统方法是将 NAS 挂载到虚拟机,然后在上面启动 Webserver。这既会浪费资源(如果该服务较少使用),也无法在数据流量大时扩展。作为替代方案,您可以编写一个 Serverless 函数,来读取存储在 NAS 文件系统上的图片,并通过 HTTP 访问域名提供图片。通过这种方式,您可以享受函数计算平台提供的即时可扩展性,同时仍只支付实际使用费用。

图片处理服务示例

此演示说明如何编写图片处理服务。

创建带有 HTTP 触发器的函数

1.登录到函数计算控制台,并选择与爬网程序函数相同的服务。
2.单击加号,为服务创建函数
3.选择创建空的 python2.7 函数,然后单击“下一步”。
4.在下拉菜单中选择 HTTP 触发器,并确保它支持 GETPOST 调用方法,然后单击“下一步”。
HTTP Trigger.jpg
5.完成该步骤的剩余部分,然后单击“确定”。
6.从同一 github 库中获取 文件,然后将目录上传到该函数。
Upload Dir.jpg

使用 Python 处理图片

函数计算平台的 python 运行时附带了许多可供使用的内置模块。在本示例中,我们同时使用 [opencv] 和 wand 来执行图片转换。

使用 Python 中的 HTTP 触发器

即使对于图片处理函数,我们也需要设置网站以处理请求。一般情况下,需要使用另一种服务(如 API 网关)来处理 HTTP 请求。在本演示中,我们将使用函数计算平台的 HTTP 触发器 功能,允许 HTTP 请求直接触发函数执行。使用 HTTP 触发器,HTTP 请求中的报头/路径/查询会全部直接传递至函数处理程序,函数可以动态返回 HTML 内容。

基于这两种功能,处理程序代码非常简单,步骤如下。

  • 从系统 environ 变量获取 HTTP 路径和查询。
  • 使用 HTTP 路径加载 NAS 文件系统上的图片。
  • 基于查询 action 应用不同的图片处理技术。
  • 将转换后的图片插入预构建的 HTML 文件并返回。

下面是处理程序逻辑的摘录,我们可以看到,wand 像加载本地系统上的常规文件一样,加载存储在 NAS 上的图片。

import cv2
from wand.image import Image

TEMPLATE = open('/code/index.html').read()
NASROOT = '/mnt/crawler'
face_cascade = cv2.CascadeClassifier('/usr/share/opencv/lbpcascades/lbpcascade_frontalface.xml')

def handler(environ, start_response):
    logger = logging.getLogger()
    context = environ['fc.context']
    path = environ.get('PATH_INFO', "/")
    fileName = NASROOT + path

    try:
        query_string = environ['QUERY_STRING']
        logger.info(query_string)
    except (KeyError):
        query_string = " "

    action = query_dist['action']

    if (action == "show"):
        with Image(filename=fileName) as fc_img:
            img_enc = base64.b64encode(fc_img.make_blob(format='png'))

    elif (action == "facedetect"):
        img = cv2.imread(fileName)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = face_cascade.detectMultiScale(gray, 1.03, 5)
        for (x, y, w, h) in faces:
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 1)
        cv2.imwrite("/tmp/dst.png", img)
        with open("/tmp/dst.png") as img_obj:
            with Image(file=img_obj) as fc_img:
                img_enc = base64.b64encode(fc_img.make_blob(format='png'))
    elif (action == "rotate"):
        assert len(queries) >= 2
        angle = query_dist['angle']
        logger.info("Rotate " + angle)
        with Image(filename=fileName) as fc_img:
            fc_img.rotate(float(angle))
            img_enc = base64.b64encode(fc_img.make_blob(format='png'))
    else:
        # demo, mixed operation

    status = '200 OK'
    response_headers = [('Content-type', 'text/html')]
    start_response(status, response_headers)
    return [TEMPLATE.replace('{fc-py}', img_enc)]
AI 代码解读

后续步骤

现在我们已准备好函数和 HTTP 触发器,我们可以尝试 [图片旋转] 或像 [面部检测]之类的高级转换。

  • URL 基于您的accountID/地域/服务/函数名构建,可参考 文章
  • 使用指向图片的本地 NAS 挂载目录的相对路径。您可以通过爬网程序日志找到 NAS 系统上的所有文件。
  • 您可以直接在函数计算控制台上编辑 Python 代码,添加更多不同的图片转换程序并乐在其中。

结论

  • 您可以阅读 博客 以进一步了解 函数计算 平台的功能。
  • 您还可以阅读官方 NAS 教程 和其他函数计算平台 文档,以了解更多令人兴奋的新功能。
  • 请通过我们的官方函数计算 论坛 或官方阿里云 Slack 频道提供反馈或建议。
相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
打赏
0
0
0
0
92
分享
相关文章
云大使 X 函数计算 FC 专属活动上线!享返佣,一键打造 AI 应用
如今,AI 技术已经成为推动业务创新和增长的重要力量。但对于许多企业和开发者来说,如何高效、便捷地部署和管理 AI 应用仍然是一个挑战。阿里云函数计算 FC 以其免运维的特点,大大降低了 AI 应用部署的复杂性。用户无需担心底层资源的管理和运维问题,可以专注于应用的创新和开发,并且用户可以通过一键部署功能,迅速将 AI 大模型部署到云端,实现快速上线和迭代。函数计算目前推出了多种规格的云资源优惠套餐,用户可以根据实际需求灵活选择。
阿里云 EMR Serverless Spark 在微财机器学习场景下的应用
面对机器学习场景下的训练瓶颈,微财选择基于阿里云 EMR Serverless Spark 建立数据平台。通过 EMR Serverless Spark,微财突破了单机训练使用的数据规模瓶颈,大幅提升了训练效率,解决了存算分离架构下 Shuffle 稳定性和性能困扰,为智能风控等业务提供了强有力的技术支撑。
118 15
美的楼宇科技基于阿里云 EMR Serverless Spark 构建 LakeHouse 湖仓数据平台
美的楼宇科技基于阿里云 EMR Serverless Spark 建设 IoT 数据平台,实现了数据与 AI 技术的有效融合,解决了美的楼宇科技设备数据量庞大且持续增长、数据半结构化、数据价值缺乏深度挖掘的痛点问题。并结合 EMR Serverless StarRocks 搭建了 Lakehouse 平台,最终实现不同场景下整体性能提升50%以上,同时综合成本下降30%。
阿里云函数计算 x NVIDIA 加速企业 AI 应用落地
阿里云函数计算与 NVIDIA TensorRT/TensorRT-LLM 展开合作,通过结合阿里云的无缝计算体验和 NVIDIA 的高性能推理库,开发者能够以更低的成本、更高的效率完成复杂的 AI 任务,加速技术落地和应用创新。
271 13
云端问道8期方案教学-基于Serverless计算快速构建AI应用开发
本文介绍了基于Serverless计算快速构建AI应用开发的技术和实践。内容涵盖四个方面:1) Serverless技术价值,包括其发展趋势和优势;2) Serverless函数计算与AI的结合,探讨AIGC应用场景及企业面临的挑战;3) Serverless函数计算AIGC应用方案,提供一键部署、模型托管等功能;4) 业务初期如何低门槛使用,介绍新用户免费额度和优惠活动。通过这些内容,帮助企业和开发者更高效地利用Serverless架构进行AI应用开发。
7分钟玩转 AI 应用,函数计算一键部署 AI 生图大模型
人工智能生成图像(AI 生图)的领域中,Stable Diffusion WebUI 以其强大的算法和稳定的输出质量而闻名。它能够快速地从文本描述中生成高质量的图像,为用户提供了一个直观且高效的创作平台。而 ComfyUI 则以其用户友好的界面和高度定制化的选项所受到欢迎。ComfyUI 的灵活性和直观性使得即使是没有技术背景的用户也能轻松上手。本次技术解决方案通过函数计算一键部署热门 AI 生图大模型,凭借其按量付费、卓越弹性、快速交付能力的特点,完美实现低成本,免运维。
尽享红利,Serverless构建企业AI应用方案与实践
本次课程由阿里云云原生架构师计缘分享,主题为“尽享红利,Serverless构建企业AI应用方案与实践”。课程分为四个部分:1) Serverless技术价值,介绍其发展趋势及优势;2) Serverless函数计算与AI的结合,探讨两者融合的应用场景;3) Serverless函数计算AIGC应用方案,展示具体的技术实现和客户案例;4) 业务初期如何降低使用门槛,提供新用户权益和免费资源。通过这些内容,帮助企业和开发者快速构建高效、低成本的AI应用。
103 12
通义灵码 x 函数计算:构建高效开发流程,加速项目交付
本方案基于通义大模型的通义灵码,提供代码生成、补全、优化及单元测试生成等能力,提升编码效率和质量。结合云效和函数计算 FC 进行代码管理、持续集成、部署发布,加速项目交付,为开发者提供智能编码、CI/CD、部署上线体验,加快产品迭代速度。
ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用
本文整理自2024年云栖大会阿里云智能集团高级技术专家金吉祥的演讲《ApsaraMQ Serverless 能力再升级,事件驱动架构赋能 AI 应用》。
206 12
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
123 1

热门文章

最新文章

相关产品

  • 函数计算