OpenFaaS实战之三:Java函数

Stella981
• 阅读 820

欢迎访问我的GitHub

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

OpenFaaS实战系列文章链接

  1. 部署
  2. 函数入门
  3. Java函数
  4. 模板操作(template)
  5. 大话watchdog

本篇概览

  1. 本文是《OpenFaaS实战》系列的第三篇,经过前文实战,咱们掌握了函数开发和部署的要领,作为一名Java程序员,当然迫切的希望用Java编写OpenFaaS函数,于是就有了本文;
  2. 本文开发一个Java函数,功能是解析请求body中的JSON字符串,再加上JVM进程ID、IP地址、当前时间一起拼成字符串,包装在JSON中返回;
  3. 平时写java代码会用到各种二方库,这里引入jackson的库,作为OpenFaaS添加依赖的参考;

源码下载

名称

链接

备注

项目主页

https://github.com/zq2599/blog\_demos

该项目在GitHub上的主页

git仓库地址(https)

https://github.com/zq2599/blog\_demos.git

该项目源码的仓库地址,https协议

git仓库地址(ssh)

git@github.com:zq2599/blog_demos.git

该项目源码的仓库地址,ssh协议

  • 这个git项目中有多个文件夹,本章的应用在openfaas文件夹下,如下图红框所示:
    OpenFaaS实战之三:Java函数
  • openfaas里面有多个子文件夹,本篇的源码在currenttime中,如下图红框:
    OpenFaaS实战之三:Java函数

创建函数

  1. 执行以下命令,即可创建名为faas-currenttime的函数,此函数的镜像前缀是bolingcavalry,语言类型为java11:

    faas-cli new faas-currenttime --lang java11 -p bolingcavalry

  2. 控制台响应如下:

    [root@node1 20]# faas-cli new faas-currenttime --lang java11 -p bolingcavalry 2020/11/20 15:47:50 No templates found in current directory. 2020/11/20 15:47:50 Attempting to expand templates from https://github.com/openfaas/templates.git

    2020/11/20 15:47:56 Fetched 12 template(s) : [csharp dockerfile go java11 java11-vert-x node node12 php7 python python3 python3-debian ruby] from https://github.com/openfaas/templates.git Folder: faas-currenttime created.


    / _ \ _ __ ___ _ __ | _| _ __ / | | | | | ' \ / _ \ ' | | / |/ _ _
    | |_| | |
    ) | / | | | | (| | (| |) | _/| ./ __|| ||| _,|_,|___/ || Function created in folder: faas-currenttime Stack file written: faas-currenttime.yml

    Notes: You have created a function using the java11 template which uses an LTS version of the OpenJDK.

  3. 当前目录已经新增了文件faas-currenttime.yml和文件夹faas-currenttime

  4. 文件夹faas-currenttime的内容如下,可见是个gradle工程:

    faas-currenttime ├── build.gradle ├── gradle │ └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main │ └── java │ └── com │ └── openfaas │ └── function │ └── Handler.java └── test └── java └── HandlerTest.java

  5. 打开build.gradle文件,添加下图红框中的内容,即jackson和common库的依赖:
    OpenFaaS实战之三:Java函数

  6. 进入文件夹faas-currenttime/src/main/java/com/openfaas/function/,可见已创建了默认的业务功能类Handler.java,打开看看OpenFaaS给的默认代码啥样的,如下所示:

    package com.openfaas.function;

    import com.openfaas.model.IHandler; import com.openfaas.model.IResponse; import com.openfaas.model.IRequest; import com.openfaas.model.Response;

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

    public IResponse Handle(IRequest req) {
    
    
    
        Response res = new Response();
            res.setBody("Hello, world!");
    
            return res;
    }
    

    }

  7. 把Handler.java的内容用以下代码替换掉,替换后的函数,其功能是取得请求参数,再把当前JVM的进程ID、IP地址、当前时间都拼接到一个字符串中返回,需要重点关注的有两点:将请求参数反序列化成Map实例,以及将Map序列化成JSON字符串返回:

    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;
    }
    

    }

  • 至此编码完成,接下来是制作镜像和部署;

部署

  1. 在faas-currenttime.yml所在目录执行以下命令,即可开始制作镜像,制作过程中会有gradle的编译过程,如果编译失败会中断镜像制作:

    faas-cli new faas-currenttime --lang java11 -p bolingcavalry

  2. 镜像制作成功时,控制台输出类似如下信息:

    Step 27/30 : ENV fprocess="java -XX:+UseContainerSupport com.openfaas.entrypoint.App" ---> Running in 0f50636cc747 Removing intermediate container 0f50636cc747 ---> 54a5c9a193c8 Step 28/30 : EXPOSE 8080 ---> Running in 3252f165af15 Removing intermediate container 3252f165af15 ---> c05afc826ec5 Step 29/30 : HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 ---> Running in 4106410be0a2 Removing intermediate container 4106410be0a2 ---> 6d95b73b5f33 Step 30/30 : CMD ["fwatchdog"] ---> Running in 1606dbcd7003 Removing intermediate container 1606dbcd7003 ---> 99a519ab82fd Successfully built 99a519ab82fd Successfully tagged bolingcavalry/faas-currenttime:latest Image: bolingcavalry/faas-currenttime:latest built. [0] < Building faas-currenttime done in 34.94s. [0] Worker done.

    Total build time: 34.94s

  3. 将镜像推送到镜像仓库,以便Kubernetes可以下载到此镜像,我这里用的是hub.docker.com,因为我的ID是bolingcavalry,所执行以下命令即可推送成功:

    docker push bolingcavalry/faas-currenttime:latest

  4. 执行以下命令部署函数到OpenFaaS:

    faas-cli deploy -f faas-currenttime.yml

  5. 控制台响应如下,可见部署已经开始,并且给出了endpoint:

    [root@node1 20]# faas-cli deploy -f faas-currenttime.yml Deploying: faas-currenttime. WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

    Deployed. 202 Accepted. URL: http://192.168.133.187:31112/function/faas-currenttime.openfaas-fn

  6. 打开web端,在页面上可见新增的函数,验证操作如下图所示,可见入参的JSON内容可以被正常解析:
    OpenFaaS实战之三:Java函数

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

    [root@node1 20]# curl \

    -H "Content-Type: application/json"
    -X POST
    --data '{"name":"Jerry}'
    http://192.168.133.187:31112/function/faas-currenttime {

    "success":true,"message":"Hello anonymous, response from [10.233.90.79], PID [11], 2020-11-20 02:14:46"}

执行命令faas-cli deploy -f faas-currenttime.yml开始部署,控制台已经接受了部署请求,并给出了函数的endpoint:

[root@node1 20]# faas-cli deploy -f faas-currenttime.yml
Deploying: faas-currenttime.
WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

Deployed. 202 Accepted.
URL: http://192.168.133.187:31112/function/faas-currenttime.openfaas-fn

清理

  • 删除函数的命令如下,依旧是faas-currenttime.yml所在目录:

    faas-cli remove -f faas-currenttime.yml

  • 至此,最基本的Java函数的开发、部署、验证都已经完成,如果您也打算用Java开发OpenFaaS函数,希望本文能给您一些参考;

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

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

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

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

本文分享 CSDN - 程序员欣宸。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
2年前
jackson学习之九:springboot整合(配置文件)
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
2年前
jackson学习之二:jackson
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
2年前
gRPC学习之四:实战四类服务方法
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
2年前
gRPC学习之三:初试GO版gRPC开发
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
2年前
Kurento实战之一:KMS部署和体验
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
2年前
Kurento实战之二:快速部署和体验
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
2年前
gRPC学习之五:gRPC
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
2年前
Kubernetes的Group、Version、Resource学习小记
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
2年前
OpenFaaS实战之四:模板操作(template)
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
2年前
gRPC学习之二:GO的gRPC开发环境准备
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
2年前
JUnit5学习之四:按条件执行
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)