OpenFaaS实战之八:自制模板(maven+jdk8)

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

OpenFaaS实战系列文章链接

  1. 部署
  2. 函数入门
  3. Java函数
  4. 模板操作(template)
  5. 大话watchdog
  6. of-watchdog(为性能而生)
  7. java11模板解析
  8. OpenFaaS实战之八:自制模板(maven+jdk8)
  9. OpenFaaS实战之九:终篇,自制模板(springboot+maven+jdk8)

本篇概览

  • 本文是《OpenFaaS实战》系列的第八篇,经过前面的理论分析和实战练习,咱们对OpenFaaS了解得差不多了,也该搞事情了;

  • 作为一个Java程序员,经常用到jdk8、maven、springboot这些东西,自然要关注官方模板是否支持,如下图,官方文档显示对java程序员的支持力度不够:不支持java8、用的是Gradle而非maven、不支持springboot,仅用vertx框架来支持web服务:
    在这里插入图片描述

  • 既然官方模板不支持,咱们就自制模板来支持吧,本着先易后难的原则,本篇先做一个简单的模板:将官方的java11模板保持功能不变,jdk版本改造成java8,并将Gradle改成maven;

  • 不可否认jdk8和maven都已一大把年纪了,新版jdk和Gradle都是更好的选择,不过本篇的重点是如何自定义模板,所以还请您给予包容…

  • 今天要做的事情,如下图所示,咱们先做左边蓝色部分,编写模板代码,上传到github模板仓库,再做右侧绿色部分,像前面文章中使用官方模板那样去使用这个模板:
    在这里插入图片描述

  • 接下来的实战由以下内容组成:

  1. 创建java项目,作为模板的基础源码
  2. 开发Dockerfile
  3. 完成模板配置并上传
  4. 验证模板

创建java项目

  • 制作模板时最重要的就是提供完整的模板代码,接下来就来制作吧;

  • 我这边用的是IDEA,建一个空maven项目,名为java8maven,用的是JDK8:

  • 如下图,注意Language level要选择8
    在这里插入图片描述

  • pom.xml的内容如下,要注意的几个点稍后会说明:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bolingcavalry</groupId>
    <artifactId>java8maven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-math3</artifactId>
            <version>3.6.1</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.openfaas</groupId>
            <artifactId>model</artifactId>
            <version>0.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.openfaas</groupId>
            <artifactId>entrypoint</artifactId>
            <version>0.1.0</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.10</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.10</version>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.0.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.openfaas.entrypoint.App</mainClass>
                        </manifest>
                        <manifestEntries>
                            <Class-Path>.</Class-Path>
                        </manifestEntries>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
  • 上述pom.xml的内容中,有几处需要注意:
  1. openfaas的model和entrypoint这两个jar是整个服务可运行的基础;
  2. 有些常用的jar依赖也被加入了,您可以酌情自行增删;
  3. 插件maven-compiler-plugin用来指定编译时的JDK版本;
  4. 插件maven-dependency-pluginmaven-assembly-plugin用来将整个java代码和依赖库打包到一个jar文件中,这样制作Docker镜像会方便很多;
package com.openfaas.function;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.openfaas.model.IRequest;
import com.openfaas.model.IResponse;
import com.openfaas.model.Response;
import org.apache.commons.lang3.StringUtils;

import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

public class Handler extends com.openfaas.model.AbstractHandler {

    private static final String PARAM_USER_NAME = "name";

    private static final String RESPONSE_TEMPLETE = "Hello %s, response from [%s], PID [%s], %s";

    private ObjectMapper mapper = new ObjectMapper();

    /**
     * 获取本机IP地址
     * @return
     */
    public static String getIpAddress() {
        try {
            Enumeration<NetworkInterface> allNetInterfaces = NetworkInterface.getNetworkInterfaces();
            InetAddress ip = null;
            while (allNetInterfaces.hasMoreElements()) {
                NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
                if (netInterface.isLoopback() || netInterface.isVirtual() || !netInterface.isUp()) {
                    continue;
                } else {
                    Enumeration<InetAddress> addresses = netInterface.getInetAddresses();
                    while (addresses.hasMoreElements()) {
                        ip = addresses.nextElement();
                        if (ip != null && ip instanceof Inet4Address) {
                            return ip.getHostAddress();
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("IP地址获取失败" + e.toString());
        }
        return "";
    }

    /**
     * 返回当前进程ID
     * @return
     */
    private static String getPID() {
        return ManagementFactory
                .getRuntimeMXBean()
                .getName()
                .split("@")[0];
    }


    private String getUserName(IRequest req) {
        // 如果从请求body中取不到userName,就用
        String userName = null;

        try {
            Map<String, Object> mapFromStr = mapper.readValue(req.getBody(),
                    new TypeReference<Map<String, Object>>() {});

            if(null!=mapFromStr && mapFromStr.containsKey(PARAM_USER_NAME)) {
                userName = String.valueOf(mapFromStr.get(PARAM_USER_NAME));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        // 如果从请求body中取不到userName,就给个默认值
        if(StringUtils.isBlank(userName)) {
            userName = "anonymous";
        }

        return userName;
    }

    public IResponse Handle(IRequest req) {

        String userName = getUserName(req);

        System.out.println("1. ---" + userName);

        // 返回信息带上当前JVM所在机器的IP、进程号、时间
        String message = String.format(RESPONSE_TEMPLETE,
                userName,
                getIpAddress(),
                getPID(),
                new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" ).format(new Date()));

        System.out.println("2. ---" + message);

        // 响应内容也是JSON格式,所以先存入map,然后再序列化
        Map<String, Object> rlt = new HashMap<>();
        rlt.put("success", true);
        rlt.put("message", message);

        String rltStr = null;

        try {
            rltStr = mapper.writeValueAsString(rlt);
        } catch (Exception e) {
            e.printStackTrace();
        }

        Response res = new Response();
        res.setContentType("application/json;charset=utf-8");
        res.setBody(rltStr);

	    return res;
    }
}
  • pom.xml所在目录下,新建文件夹m2,里面增加maven的配置文件settings.xml,该文件是在FaaS开发过程中,制作镜像时用到的(制作镜像时会编译构建java项目),强烈建议在里面配置好您的maven私服,或者阿里云镜像,这样制作镜像时会快很多,我这里已经配置了阿里云镜像,依然耗时四分多钟(如下图),所以如果您有nexus3私服一定要优先考虑:
    在这里插入图片描述

  • 至此,编码工作已完成,可见这就是个普通maven工程,来试试能不能正常运行;

  • 执行命令mvn clean package -U -DskipTests,成功后会在target目录生成文件java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar

  • 运行上述jar文件,命令是java -jar java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar

  • 上述jar运行起来后会监听8082端口的POST请求,我这里用postman来试试,如下图,可以收到后台返回的最新数据:
    在这里插入图片描述

  • 后台控制台也会打印出预期的内容:
    在这里插入图片描述

  • 代码写完了,接下来要考虑的如何制作Docker镜像,即Dockerfile的编写;

开发Dockerfile

  • 前面的实战中咱们已经体验过,开发FaaS的时候会将代码编译构建制作成镜像,因此对应的Dockerfile也要准备好,下面是完整的Dockerfile内容,已经添加详细的注释,就不再赘述了:
# 用maven镜像作为基础镜像,用于编译构建java项目
FROM maven:3.6.3-openjdk-8 as builder

WORKDIR /home/app

# 将整个项目都复制到/home/app目录下
COPY . /home/app/

# 进入pom.xml所在目录执行构建命令,指定m2/settings.xml文件作为配置文件,
# 请在settings.xml中配置好私服,否则构建速度极慢
RUN cd function && mvn clean package -U -DskipTests --settings ./m2/settings.xml 

# of-watchdog里面有二进制文件watchdog,制作镜像时要用到
FROM openfaas/of-watchdog:0.7.6 as watchdog

# openjdk镜像是容器的运行环境
FROM openjdk:8-jre-slim as ship

# 为了安全起见,在生产环境运行容器时不要用指root帐号和群组
RUN addgroup --system app \
    && adduser --system --ingroup app app

# 从of-watchdog镜像中复制二进制文件fwatchdog,这是容器的启动进程
COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog

# 赋予可执行权限
RUN chmod +x /usr/bin/fwatchdog

WORKDIR /home/app

# 前面用maven编译构建完毕后,这里将构建结果复制到镜像中
COPY --from=builder /home/app/function/target/java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar ./java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar
# 指定容器的运行帐号
user app

# 指定容器的工作目录
WORKDIR /home/app/

# fwatchdog收到web请求后的转发地址,java进程监听的就是这个端口
ENV upstream_url="http://127.0.0.1:8082"

# 运行模式是http
ENV mode="http"

# 拉起业务进程的命令,这里就是启动java进程
ENV fprocess="java -jar java8maven-1.0-SNAPSHOT-jar-with-dependencies.jar"

# 容器对外暴露的端口,也就是fwatchdog进程监听的端口
EXPOSE 8080

# 健康检查
HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1

# 容器启动命令,这里是执行二进制文件fwatchdog
CMD ["fwatchdog"]

模板配置

  • 现在材料已经准备完毕了,再整理一下准备提交到github上,就可以作为OpenFaaS模板使用了;
  1. 新建一个文件夹,名为simplejava8
  2. simplejava8目录下新建文件template.yml,内容如下:
language: simplejava8
welcome_message: |
  You have created a function using the java8 and maven template
  1. 将前面的Dockerfile文件复制到simplejava8目录下;
  2. 前面咱们创建的maven工程,最外层的文件夹名为java8maven,请将此文件夹改名为function,然后将整个文件夹都复制到simplejava8目录下;
  3. 此刻的simplejava8目录下应该是这些内容:
[root@hedy 002]# tree simplejava8
simplejava8
├── Dockerfile
├── function
│   ├── java8maven.iml
│   ├── m2
│   │   └── settings.xml
│   ├── pom.xml
│   └── src
│       ├── main
│       │   ├── java
│       │   │   └── com
│       │   │       └── openfaas
│       │   │           └── function
│       │   │               └── Handler.java
│       │   └── resources
│       └── test
│           └── java
└── template.yml

11 directories, 6 files
  1. 将这些内容全部上传到github上,我这里路径是https://github.com/zq2599/openfaas-templates/tree/master/template,这里面已经有三个模板了,本次新增的如下图红框:
    在这里插入图片描述
  • 至此,模板制作完成,接下来验证此模板是否可用;

验证模板

  • 接下来要做的,就是下图右侧的绿色部分:
    在这里插入图片描述

  • 登录一台配好OpenFaaS客户端的电脑,找个干净目录执行以下命令,将github上所有模板下载下来:

faas template pull https://github.com/zq2599/openfaas-templates
  • 控制台响应如下,提示下载了三个模板,符合预期:
[root@hedy 07]# faas template pull https://github.com/zq2599/openfaas-templates
Fetch templates from repository: https://github.com/zq2599/openfaas-templates at 
2021/03/07 08:44:29 Attempting to expand templates from https://github.com/zq2599/openfaas-templates
2021/03/07 08:44:32 Fetched 3 template(s) : [dockerfile java11extend simplejava8] from https://github.com/zq2599/openfaas-templates
  • faas new --list查看列表如下:
[root@hedy 07]# faas new --list
Languages available as templates:
- dockerfile
- java11extend
- simplejava8
  • 看看template/simplejava8目录下的内容,和前面上传的一模一样:
[root@hedy 07]# tree template/simplejava8/
template/simplejava8/
├── Dockerfile
├── function
│   ├── java8maven.iml
│   ├── m2
│   │   └── settings.xml
│   ├── pom.xml
│   └── src
│       └── main
│           └── java
│               └── com
│                   └── openfaas
│                       └── function
│                           └── Handler.java
└── template.yml

8 directories, 6 files
  • 有了模板就可以创建函数了,执行以下命令创建名为faas-simplejava8demo的函数:
faas-cli new faas-simplejava8demo --lang simplejava8 -p bolingcavalry
  • 控制台提示如下,此时当前目录下新增文件夹faas-simplejava8demo,这就是新建函数的代码目录:
[root@hedy 07]# faas-cli new faas-simplejava8demo --lang simplejava8 -p bolingcavalry
Folder: faas-simplejava8demo created.
  ___                   _____           ____
 / _ \ _ __   ___ _ __ |  ___|_ _  __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) |  __/ | | |  _| (_| | (_| |___) |
 \___/| .__/ \___|_| |_|_|  \__,_|\__,_|____/
      |_|


Function created in folder: faas-simplejava8demo
Stack file written: faas-simplejava8demo.yml

Notes:
You have created a function using the java8 and maven template
[root@hedy 07]# ls
faas-simplejava8demo  faas-simplejava8demo.yml  template
  • 文件夹faas-simplejava8demo的内容如下,现在妥了,用IDEA等IDE工具以maven工程形式导入,然后根据业务需求修改这个工程即可:
[root@hedy 07]# tree faas-simplejava8demo
faas-simplejava8demo
├── java8maven.iml
├── m2
│   └── settings.xml
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── openfaas
                    └── function
                        └── Handler.java

7 directories, 4 files
  • 现在可以开发业务了,这里为了测试,新增了一行代码,如下图红框:
    在这里插入图片描述
  • 开始编译构建吧,执行以下命令:
faas-cli build -f ./faas-simplejava8demo.yml
  • 构建完成后将镜像推送到镜像仓库,以便Kubernetes可以下载到此镜像,我这里用的是hub.docker.com,因为我的ID是bolingcavalry,所执行以下命令即可推送成功(要先执行docker login命令登录):
docker push bolingcavalry/faas-simplejava8demo:latest
  • 执行以下命令部署函数到OpenFaaS:
faas-cli deploy -f faas-simplejava8demo.yml
  • 控制台响应如下,可见部署已经开始,并且给出了endpoint:
[root@hedy 07]# faas-cli deploy -f faas-simplejava8demo.yml
Deploying: faas-simplejava8demo.
WARNING! You are not using an encrypted connection to the gateway, consider using HTTPS.

Deployed. 202 Accepted.
URL: http://192.168.50.75:31112/function/faas-simplejava8demo.openfaas-fn
  • 打开web端,在页面上可见新增的函数,验证操作如下图所示,可见入参的JSON内容可以被正常解析:
    在这里插入图片描述

  • 也可以在控制台用curl命令测试:

[root@hedy 07]# curl \
> -H "Content-Type: application/json" \
> -X POST \
> --data '{"name":"Jerry}' \
> http://192.168.50.75:31112/function/faas-simplejava8demo
{"success":true,"foo":"bar","message":"Hello anonymous, response from [10.244.0.168], PID [14], 2021-03-07 03:32:15"}

清理

  • 删除函数的命令如下,依旧是faas-simplejava8demo.yml所在目录:
faas-cli remove -f faas-simplejava8demo.yml
  • 至此,自制的maven+jdk8的模板,从开发到验证咱们已经全部走了一遍,相信您对OpenFaaS的理解也已经更加全面和深入了,本篇是为开发模板练手用的,实用价值不大,接下来的文章咱们要做个实用的模板:jdk8+maven+springboot

你不孤单,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢迎关注公众号:程序员欣宸

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界…

已标记关键词 清除标记
相关推荐
DirectX修复工具(DirectX Repair)是一款系统级工具软件,简便易用。本程序为绿色版,无需安装,可直接运行。 本程序的主要功能是检测当前系统的DirectX状态,如果发现异常则进行修复。程序主要针对0xc000007b问题设计,可以完美修复该问题。本程序中包含了最新版的DirectX redist(Jun2010),并且全部DX文件都有Microsoft的数字签名,安全放心。 本程序为了应对一般电脑用户的使用,采用了易用的一键式设计,只要点击主界面上的“检测并修复”按钮,程序就会自动完成校验、检测、下载、修复以及注册的全部功能,无需用户的介入,大大降低了使用难度。在常规修复过程中,程序还会自动检测DirectX加速状态,在异常时给予用户相应提示。 本程序适用于多个操作系统,如Windows XP(需先安装.NET 2.0,详情请参阅“致Windows XP用户.txt”文件)、Windows Vista、Windows 7、Windows 8、Windows 8.1、Windows 8.1 Update、Windows 10,同时兼容32位操作系统和64位操作系统。本程序会根据系统的不同,自动调整任务模式,无需用户进行设置。 本程序的V4.0版分为标准版、增强版以及在线修复版。所有版本都支持修复DirectX的功能,而增强版则额外支持修复c++的功能。在线修复版功能与标准版相同,但其所需的数据包需要在修复时自动下载。各个版本之间,主程序完全相同,只是其配套使用的数据包不同。因此,标准版和在线修复版可以通过补全扩展包的形式成为增强版。本程序自V3.5版起,自带扩展功能。只要在主界面的“工具”菜单下打开“选项”对话框,找到“扩展”标签,点击其中的“开始扩展”按钮即可。扩展过程需要Internet连接,扩展成功后新的数据包可自动生效。扩展用时根据网络速度不同而不同,最快仅需数秒,最慢需要数分钟,烦请耐心等待。如扩展失败,可点击“扩展”界面左上角小锁图标切换为加密连接,即可很大程度上避免因防火墙或其他原因导致的连接失败。 本程序自V2.0版起采用全新的底层程序架构,使用了异步多线程编程技术,使得检测、下载、修复单独进行,互不干扰,快速如飞。新程序更改了自我校验方式,因此使用新版本的程序时不会再出现自我校验失败的错误;但并非取消自我校验,因此程序安全性与之前版本相同,并未降低。 程序有更新系统c++功能。由于绝大多数软件运行时需要c++的支持,并且c++的异常也会导致0xc000007b错误,因此程序在检测修复的同时,也会根据需要更新系统中的c++组件。自V3.2版本开始使用了全新的c++扩展包,可以大幅提高工业软件修复成功的概率。修复c++的功能仅限于增强版,标准版及在线修复版在系统c++异常时(非丢失时)会提示用户使用增强版进行修复。除常规修复外,新版程序还支持C++强力修复功能。当常规修复无效时,可以到本程序的选项界面内开启强力修复功能,可大幅提高修复成功率。请注意,请仅在常规修复无效时再使用此功能。 程序有两种窗口样式。正常模式即默认样式,适合绝大多数用户使用。另有一种简约模式,此时窗口将只显示最基本的内容,修复会自动进行,修复完成10秒钟后会自动退出。该窗口样式可以使修复工作变得更加简单快速,同时方便其他软件、游戏将本程序内嵌,即可进行无需人工参与的快速修复。开启简约模式的方法是:打开程序所在目录下的“Settings.ini”文件(如果没有可以自己创建),将其中的“FormStyle”一项的值改为“Simple”并保存即可。 新版程序支持命令行运行模式。在命令行中调用本程序,可以在路径后直接添加命令进行相应的设置。常见的命令有7类,分别是设置语言的命令、设置窗口模式的命令,设置安全级别的命令、开启强力修复的命令、设置c++修复模式的命令、控制Direct加速的命令、显示版权信息的命令。具体命令名称可以通过“/help”或“/?”进行查询。 程序有高级筛选功能,开启该功能后用户可以自主选择要修复的文件,避免了其他不必要的修复工作。同时,也支持通过文件进行辅助筛选,只要在程序目录下建立“Filter.dat”文件,其中的每一行写一个需要修复文件的序号即可。该功能仅针对高级用户使用,并且必须在正常窗口模式下才有效(简约模式时无效)。 本程序有自动记录日志功能,可以记录每一次检测修复结果,方便在出现问题时,及时分析和查找原因,以便找到解决办法。 程序的“选项”对话框中包含了7项高级功能。点击"常规”选项卡可以调整程序的基本运行情况,包括日志记录、安全级别控制、调试模式开启等。只有开启调试模式后才能在C
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页