美文网首页
OpenFaaS实战之三:Java函数

OpenFaaS实战之三:Java函数

作者: 程序员欣宸 | 来源:发表于2021-05-18 07:25 被阅读0次

    欢迎访问我的GitHub

    https://github.com/zq2599/blog_demos

    内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;

    OpenFaaS实战系列文章链接

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

    本篇概览

    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项目中有多个文件夹,本章的应用在<font color="blue">openfaas</font>文件夹下,如下图红框所示:
    在这里插入图片描述
    • <font color="blue">openfaas</font>里面有多个子文件夹,本篇的源码在<font color="blue">currenttime</font>中,如下图红框:
    在这里插入图片描述

    创建函数

    1. 执行以下命令,即可创建名为<font color="blue">faas-currenttime</font>的函数,此函数的镜像前缀是<font color="blue">bolingcavalry</font>,语言类型为<font color="red">java11</font>:
    faas-cli new faas-currenttime --lang java11 -p bolingcavalry
    
    1. 控制台响应如下:
    [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.
    
    1. 当前目录已经新增了文件<font color="blue">faas-currenttime.yml</font>和文件夹<font color="blue">faas-currenttime</font>
    2. 文件夹<font color="blue">faas-currenttime</font>的内容如下,可见是个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
    
    1. 打开<font color="red">build.gradle</font>文件,添加下图红框中的内容,即<font color="blue">jackson</font>和<font color="blue">common</font>库的依赖:
    在这里插入图片描述
    1. 进入文件夹<font color="blue">faas-currenttime/src/main/java/com/openfaas/function/</font>,可见已创建了默认的业务功能类<font color="red">Handler.java</font>,打开看看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;
        }
    }
    
    1. 把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. 在<font color="blue">faas-currenttime.yml</font>所在目录执行以下命令,即可开始制作镜像,制作过程中会有gradle的编译过程,如果编译失败会中断镜像制作:
    faas-cli build -f ./faas-currenttime.yml
    
    1. 镜像制作成功时,控制台输出类似如下信息:
    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
    
    1. 将镜像推送到镜像仓库,以便Kubernetes可以下载到此镜像,我这里用的是hub.docker.com,因为我的ID是<font color="blue">bolingcavalry</font>,所执行以下命令即可推送成功:
    docker push bolingcavalry/faas-currenttime:latest
    
    1. 执行以下命令部署函数到OpenFaaS:
    faas-cli deploy -f faas-currenttime.yml
    
    1. 控制台响应如下,可见部署已经开始,并且给出了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
    
    1. 打开web端,在页面上可见新增的函数,验证操作如下图所示,可见入参的JSON内容可以被正常解析:
    在这里插入图片描述
    1. 也可以在控制台用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"}
    
    1. 执行命令<font color="blue">faas-cli deploy -f faas-currenttime.yml</font>开始部署,控制台已经接受了部署请求,并给出了函数的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
    

    清理

    • 删除函数的命令如下,依旧是<font color="blue">faas-currenttime.yml</font>所在目录:
    faas-cli remove -f faas-currenttime.yml
    
    • 至此,最基本的Java函数的开发、部署、验证都已经完成,如果您也打算用Java开发OpenFaaS函数,希望本文能给您一些参考;

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

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

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

    微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...
    https://github.com/zq2599/blog_demos

    相关文章

      网友评论

          本文标题:OpenFaaS实战之三:Java函数

          本文链接:https://www.haomeiwen.com/subject/zwcejltx.html