本文是《OpenFaaS梳理》系列的第三篇,在前面两节搭建openfaas部署环境并且成功将一个函数发布到openfaas之后,我们就可以通过前端页面进行已经发布函数信息的查看,其中包括已经发布的函数列表以及对单个函数通过Invoke function 以request请求进行测试,类似于postman进行访问。不过身为开发人员当然不止想通过工具进行访问,官方提供了OpenFaaS API Gateway 通过接口进行openfaas对函数从部署(deploy)、触发(invoke)、扩缩容(scale)等操作,本篇的是通过研究熟悉OpenFaaS API Gateway进行函数的相关操作。
目录
OpenFaaS Gateway
官方连接:OpenFaaS API Gateway / Portal
官方的介绍: Gateway 是一个路径,通过该路径用户可以通过对Prometheus的访问,对已经发布的函数进行监控;同时,gateway提供了一个UI界面,在该界面用户可以管理已经发布的函数,同时该界面进行触发测试。
此外,Gateway 通过 Kubernetes的API命令修改服务的副本数,实现对已经发布服务的扩缩容。
总之,Gateway具有如下作用:
1.它是openfaas内置的UI界面
2.通过它,可以从Function Store部署函数或触发已经发布的函数
3.通过Prometheus对函数进行监控
4.通过AlertManager和Prometheus实现服务的自动扩缩容
5.提供Swagger提供REST API
Swagger.yml
上图中,介绍到官方提供了对Gateway的访问的swagger.yaml,我们通过在线编辑器进行打开,可以看到官方提供了很多的方法进行对接口的访问。
在线编辑器:swaggerIO
Swagger接口
下面我们挑选几个接口进行测试(由于该接口都是get和post接口,本文写了一个工具类管理基本的get和post请求,请参见最后):
0.首先是demo的一些基本常量
private static Logger logger = LoggerFactory.getLogger(OpenfaasFunctionDemo.class);
private static final String BASICAUTH = "admin:openfaas";
private static final String OPENFAAS_URL = "http://192.168.79.139:31112/";
private static final String SCALE_FUNCTION = "system/scale-function";
private static final String SYSTEM_FUNCTION = "system/function";
private static final String SYSTEM_FUNCTIONS = "system/functions";
private static final String SYSTEM_LOGS = "system/logs";
private static final String BACKSLASH = "/";
其中最重要的是BASICAUTH 是openfaas的用户名和密码;OPENFAAS_URL是openfaas gateway的访问地址;
1.查看发布函数的详细信息
代码段:
/**
* 获取所有的函数列表
*/
public static void listFunctionGet() {
String url = OPENFAAS_URL + SYSTEM_FUNCTIONS;
String data = HttpClientUtils.httpGet(url, BASICAUTH);
System.out.println("遍历服务列表:" + data);
}
测试返回结果:
[
{
"name": "add-demo",
"image": "192.168.79.131:8443/openfaas/add-demo:latest",
"namespace": "openfaas-fn",
"envProcess": "python index.py",
"labels": {
"faas_function": "add-demo"
},
"annotations": {
"prometheus.io.scrape": "false"
},
"replicas": 1,
"availableReplicas": 1,
"createdAt": "2022-09-16T14:41:32Z"
},
{
"name": "test-deploy-sync",
"image": "192.168.79.131:8443/openfaas/add-demo:latest",
"namespace": "openfaas-fn",
"labels": {
"faas_function": "test-deploy-sync"
},
"annotations": {
"prometheus.io.scrape": "false"
},
"limits": {
"memory": "256M",
"cpu": "1"
},
"requests": {
"memory": "256M",
"cpu": "1"
},
"invocationCount": 1,
"replicas": 1,
"availableReplicas": 1,
"createdAt": "2022-09-21T11:51:21Z"
}
]
2.查看发布函数的详细信息
代码段:
/**
* 启动函数
* @param serviceName
*/
public static void startFunctionPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
int instances = 2;
param.put("service", serviceName);
param.put("replicas", instances);
String url = OPENFAAS_URL + SCALE_FUNCTION + BACKSLASH + serviceName;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("启动服务结果:" + result);
}
/**
* 停止函数
* @param serviceName
*/
public static void stopFunctionPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
int instances = 0;
param.put("service", serviceName);
param.put("replicas", instances);
String url = OPENFAAS_URL + SCALE_FUNCTION + BACKSLASH + serviceName;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("停止服务结果:" + result);
}
其实质就是通过调用 system/scale-function 接口进行扩缩容改变实例的数量,进行函数的启停。
3.发布函数
代码段:
/**
* 部署触发器函数
*
* @param serviceName
* @param imageFullName
* @param cpu
* @param memory
* @param timeOut
* @param instances
*/
public static void deploySyncFunctionPost(String serviceName, String imageFullName, String cpu, String memory, String timeOut, String instances) {
HashMap<String, Object> param = new HashMap<>();
HashMap<String, Object> requestParam = new HashMap<>();
HashMap<String, Object> labelsParam = new HashMap<>();
HashMap<String, Object> limitsParam = new HashMap<>();
requestParam.put("memory", memory + "M");
requestParam.put("cpu", cpu);
limitsParam.put("memory", memory + "M");
limitsParam.put("cpu", cpu);
param.put("service", serviceName);
param.put("image", imageFullName);
param.put("limits", limitsParam);
param.put("requests", requestParam);
labelsParam.put("com.openfaas.scale.min", instances);
param.put("label", labelsParam);
HashMap<Object, Object> envVarsMap = new HashMap<>();
envVarsMap.put("userName", "hello");
param.put("envVars", envVarsMap);
param.put("replicas", instances);
//http://192.168.79.139:31112/system/functions
String url = OPENFAAS_URL + SYSTEM_FUNCTIONS;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("启动同步服务: " + result);
}
/**
* 测试部署函数
*/
public static void testDeployFunction() {
String serviceName = "test-deploy-sync";
String imageFullName = "192.168.79.131:8443/openfaas/add-demo:latest";
String envVars = "";
String cpu = "1";
String memory = "256";
String timeOut = "3000";
String instance = "2";
deploySyncFunctionPost(serviceName, imageFullName, cpu, memory, timeOut, instance);
}
发布后再前端可见:
4.触发函数
代码段:
public static void invokeFunctionPost(String serviceName,String msg) {
HashMap<String, Object> param = new HashMap<>();
param.put("name", serviceName);
param.put("hello", msg);
// http://192.168.79.139:31112/system/logs
String url = OPENFAAS_URL + FUNCTION + BACKSLASH + serviceName;
String data = HttpClientUtils.httpGet(url, param, BASICAUTH);
System.out.println("获取日志" + data);
}
返回结果:
由于时间有限,针对gatewayapi的测试访问就到这里,后面有机会再补充其他功能接口的测试。
完整代码
HttpClientUtils:
package com.openfaas.utils;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class HttpClientUtils {
/**
* 将传入的键值对转化为NameValuePair参数集
*
* @param paramsMap
* @return
*/
private static List<NameValuePair> getParamList(Map<String, Object> paramsMap) {
if (paramsMap == null || paramsMap.size() == 0) {
return Collections.emptyList();
}
ArrayList<NameValuePair> params = new ArrayList<>();
for (Map.Entry<String, Object> set : paramsMap.entrySet()) {
String key = set.getKey();
if (key != null) {
String value = set.getValue() == null ? "" : set.getValue().toString();
params.add(new BasicNameValuePair(key, value));
}
}
return params;
}
public static String httpGet(String url, String basicAuth){
return httpGet(url,new HashMap<>(),String.valueOf(StandardCharsets.UTF_8),basicAuth);
}
public static String httpGet(String url, Map<String, Object> params, String basicAuth) {
return httpGet(url,params,String.valueOf(StandardCharsets.UTF_8),basicAuth);
}
public static String httpGet(String url, Map<String, Object> params, String charset, String basicAuth) {
if (StringUtils.isBlank(url)) {
return null;
}
List<NameValuePair> gparams = getParamList(params);
if (gparams != null && gparams.size() > 0) {
charset = charset == null ? String.valueOf(StandardCharsets.UTF_8) : charset;
String formatParams = URLEncodedUtils.format(gparams, charset);
url = url.indexOf("?") < 0 ? (url + "?" + formatParams) : (url.substring(0, url.indexOf("?") + 1) + formatParams);
}
String responseBody = null;
HttpClient httpClient = null;
HttpGet httpGet = null;
try {
if (url.startsWith("https")) {
// httpClient = new SSLClinet();
} else {
httpClient = new DefaultHttpClient();
}
httpGet = new HttpGet(url);
RequestConfig config = RequestConfig.custom().setConnectTimeout(50000).build();
httpGet.setConfig(config);
if (StringUtils.isNotBlank(basicAuth)) {
httpGet.setHeader(HttpHeaders.AUTHORIZATION,
"Basic " + new String(Base64.getEncoder().encode(basicAuth.getBytes(StandardCharsets.UTF_8))));
}
HttpResponse response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
responseBody = EntityUtils.toString(entity);
}
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
abortConnection(httpGet, httpClient);
}
return responseBody;
}
public static String httpPostByBody(String url, Map<String, Object> requestBodyMap, String basicAuth) {
String body = null;
HttpClient httpClient = null;
HttpPost httpPost = null;
try {
if (url.startsWith("https")) {
// httpClient = new SSLClinet();
} else {
httpClient = new DefaultHttpClient();
}
httpPost = new HttpPost(url);
// 添加body
if (requestBodyMap != null) {
String requestBody = JSON.toJSONString(requestBodyMap);
System.out.println("requestBody:" + requestBody);
ByteArrayEntity arrayEntity = new ByteArrayEntity(requestBody.getBytes(StandardCharsets.UTF_8));
arrayEntity.setContentType("application/json");
httpPost.setEntity(arrayEntity);
}
if (StringUtils.isNotBlank(basicAuth)) {
httpPost.setHeader(HttpHeaders.AUTHORIZATION,
"Basic " + new String(Base64.getEncoder().encode(basicAuth.getBytes(StandardCharsets.UTF_8))));
}
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity != null) {
body = EntityUtils.toString(entity);
}
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
abortConnection(httpPost, httpClient);
}
return body;
}
public static String httpDeleteByBody(String url, Map<String, Object> requestBodyMap, String basicAuth) {
String body = null;
HttpClient httpClient = null;
MineHttpDelete httpDelete = null;
try {
if (url.startsWith("https")) {
// httpClient = new SSLClinet();
} else {
httpClient = new DefaultHttpClient();
}
httpDelete = new MineHttpDelete(url);
// 添加body
if (requestBodyMap != null) {
String requestBody = JSON.toJSONString(requestBodyMap);
System.out.println("requestBody:" + requestBody);
ByteArrayEntity arrayEntity = new ByteArrayEntity(requestBody.getBytes(StandardCharsets.UTF_8));
arrayEntity.setContentType("application/json");
httpDelete.setEntity(arrayEntity);
}
if (StringUtils.isNotBlank(basicAuth)) {
httpDelete.setHeader(HttpHeaders.AUTHORIZATION,
"Basic" + new String(Base64.getEncoder().encode(basicAuth.getBytes(StandardCharsets.UTF_8))));
}
HttpResponse response = httpClient.execute(httpDelete);
HttpEntity entity = response.getEntity();
if (entity != null) {
body = EntityUtils.toString(entity);
}
EntityUtils.consume(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
abortConnection(httpDelete, httpClient);
}
return body;
}
/**
* 释放
*
* @param httpPost
* @param httpClient
*/
private static void abortConnection(HttpRequestBase httpPost, HttpClient httpClient) {
if (httpPost != null) {
httpPost.abort();
}
if (httpClient != null) {
httpClient.getConnectionManager().shutdown();
}
}
}
OpenfaasFunctionDemo:
package com.openfaas;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.openfaas.utils.HttpClientUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class OpenfaasFunctionDemo {
private static Logger logger = LoggerFactory.getLogger(OpenfaasFunctionDemo.class);
private static final String BASICAUTH = "admin:openfaas";
private static final String OPENFAAS_URL = "http://192.168.79.139:31112/";
private static final String SCALE_FUNCTION = "system/scale-function";
private static final String SYSTEM_FUNCTION = "system/function";
private static final String FUNCTION = "function";
private static final String SYSTEM_FUNCTIONS = "system/functions";
private static final String SYSTEM_LOGS = "system/logs";
private static final String BACKSLASH = "/";
/**
* 获取所有的函数列表
*/
public static void listFunctionGet() {
String url = OPENFAAS_URL + SYSTEM_FUNCTIONS;
String data = HttpClientUtils.httpGet(url, BASICAUTH);
System.out.println("遍历服务列表:" + data);
}
/**
* 启动函数
* @param serviceName
*/
public static void startFunctionPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
int instances = 2;
param.put("service", serviceName);
param.put("replicas", instances);
String url = OPENFAAS_URL + SCALE_FUNCTION + BACKSLASH + serviceName;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("启动服务结果:" + result);
}
/**
* 停止函数
*
* @param serviceName
*/
public static void stopFunctionPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
param.put("service", serviceName);
param.put("replicas", 0);
String url = OPENFAAS_URL + SCALE_FUNCTION + BACKSLASH + serviceName;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("停止服务结果:" + result);
}
/**
* 删除函数
*
* @param serviceName
*/
public static void deleteFunctionPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
param.put("service", serviceName);
String url = OPENFAAS_URL + SCALE_FUNCTION;
String result = HttpClientUtils.httpDeleteByBody(url, param, BASICAUTH);
System.out.println("删除服务结果:" + result);
}
/**
* 获取服务状态
*
* @param serviceName
*/
public static void getFunctionStatus(String serviceName) {
String url = OPENFAAS_URL + SYSTEM_FUNCTION + BACKSLASH + serviceName;
String data = HttpClientUtils.httpGet(url, BASICAUTH);
System.out.println("data:" + data);
JSONObject jsonObject = JSON.parseObject(data);
if (jsonObject != null && jsonObject.containsKey("replicas") && jsonObject.containsKey("availableReplicas")) {
int availableReplicas = jsonObject.getInteger("availableReplicas");
if (availableReplicas > 0) {
System.out.println("服务状态:已经启动。");
} else {
System.out.println("服务状态:未启动。");
}
} else {
System.out.println("服务状态:未启动。");
}
if (StringUtils.isNotBlank(data)) {
System.out.println("服务状态:" + data);
}
}
/**
* 部署触发器函数
*
* @param serviceName
* @param imageFullName
* @param cpu
* @param memory
* @param timeOut
* @param instances
*/
public static void deploySyncFunctionPost(String serviceName, String imageFullName, String cpu, String memory, String timeOut, String instances) {
HashMap<String, Object> param = new HashMap<>();
HashMap<String, Object> requestParam = new HashMap<>();
HashMap<String, Object> labelsParam = new HashMap<>();
HashMap<String, Object> limitsParam = new HashMap<>();
requestParam.put("memory", memory + "M");
requestParam.put("cpu", cpu);
limitsParam.put("memory", memory + "M");
limitsParam.put("cpu", cpu);
param.put("service", serviceName);
param.put("image", imageFullName);
param.put("limits", limitsParam);
param.put("requests", requestParam);
labelsParam.put("com.openfaas.scale.min", instances);
param.put("label", labelsParam);
HashMap<Object, Object> envVarsMap = new HashMap<>();
envVarsMap.put("userName", "hello");
param.put("envVars", envVarsMap);
param.put("replicas", instances);
//http://192.168.79.139:31112/system/functions
String url = OPENFAAS_URL + SYSTEM_FUNCTIONS;
String result = HttpClientUtils.httpPostByBody(url, param, BASICAUTH);
System.out.println("启动同步服务: " + result);
}
/**
* 测试部署函数
*/
public static void testDeployFunction() {
String serviceName = "test-deploy-sync";
String imageFullName = "192.168.79.131:8443/openfaas/add-demo:latest";
String envVars = "";
String cpu = "1";
String memory = "256";
String timeOut = "3000";
String instance = "2";
deploySyncFunctionPost(serviceName, imageFullName, cpu, memory, timeOut, instance);
}
/**
* 获取部署函数的日志
*
* @param serviceName
*/
public static void getFunctionLogsPost(String serviceName) {
HashMap<String, Object> param = new HashMap<>();
param.put("name", serviceName);
param.put("tail", Long.MAX_VALUE);
// http://192.168.79.139:31112/system/logs
String url = OPENFAAS_URL + SYSTEM_LOGS;
String data = HttpClientUtils.httpGet(url, param, BASICAUTH);
System.out.println("获取日志" + data);
}
public static void invokeFunctionPost(String serviceName,String msg) {
HashMap<String, Object> param = new HashMap<>();
param.put("name", serviceName);
param.put("hello", msg);
// http://192.168.79.139:31112/system/logs
String url = OPENFAAS_URL + FUNCTION + BACKSLASH + serviceName;
String data = HttpClientUtils.httpGet(url, param, BASICAUTH);
System.out.println("获取日志" + data);
}
public static void main(String[] args) {
String serviceName = "add-demo";
String message = "陌生人";
// listFunctionGet();
// startFunctionPost(serviceName);
// getFunctionLogsPost(serviceName);
// getFunctionStatus(serviceName);
// testDeployFunction();
invokeFunctionPost(serviceName,message);
}
}
总结
本篇介绍了openfaas的gateway下的api接口提供的功能及简单的访问,实现了函数发布和触发等功能接口的调用。对于原理并未深究,感兴趣的同学可以参考以下文章进行了解。