【Harmony】端云一体化(云函数)
一、云函数的概述
1、什么是云函数
- 官方解释
云函数是一项Serverless计算服务,提供FaaS(Function as a Service)能力,一方面云函数将开发测试的对象聚焦到函数级别,可以帮助您大幅简化应用开发与运维相关的事务,另一方面您可以通过在应用中集成云函数SDK,便捷操作云数据库、云存储等,提升业务功能构建的便利性。云函数可以根据函数的实际流量对函数进行弹性伸缩,您无需对服务器资源进行管理,解决了开发者运维管理的难题。
- 通俗理解
云函数是一种无需管理服务器的计算服务,可以让你只专注于编写函数代码,不需要处理服务器的复杂管理。通过云函数,你可以轻松连接云数据库和云存储,方便地构建应用功能。而且,它会根据你应用的访问量自动调整资源,不用你手动管理服务器资源,让你更专注于开发应用本身。
补充:什么是Serverless?
Serverless 是一种无需管理服务器和运行环境的计算服务。你可以把它想象成一种按需的云计算资源,就像打开水龙头就有水一样,你只需要调用API,云服务就会为你分配所需的计算资源,而无需提前准备服务器或部署应用。Serverless服务实现了计算资源的按需分配,简化了应用的部署和维护,使得开发者可以更加专注于业务逻辑的开发。
2、云函数作用
-
简化开发与运维
云函数提供了高效可靠的函数开发与运行框架,替开发者完全解决传统应用开发与运维中的诸多复杂事务(如服务器配置与管理、代码部署、负载均衡、弹性伸缩、高可用保证等等),您只需聚焦业务逻辑、开发并上传函数代码,即可构建高可用、可伸缩的Serverless应用。
-
扩展周边服务
云函数作为Serverless的核心与枢纽,支持方便连接和扩展周边云服务能力,您可以像拼搭积木一样自由便捷地组织各项服务来实现业务逻辑。
3、云函数工作原理
该图片展示了鸿蒙端云一体化的工作原理。从左到右,上到下,我们可以看到以下主要组件和它们之间的关系:
-
开发云函数:这表示开发者可以在本地或云端环境中编写和部署函数。这些函数可以通过SDK(软件开发工具包)被集成到各种应用程序中,如移动端、桌面端等。
-
函数仓库:这是一个存储和管理函数的地方,开发者可以将他们编写的函数上传到这里。
-
触发器类型:当特定事件发生时,会触发函数的执行。例如,可以通过HTTP请求、消息队列、定时器等方式触发函数。
-
云函数:这是运行在云端的服务,可以自动伸缩以应对高并发请求。开发者发布的函数会被部署到云函数上,供全球用户访问。
-
函数运行平台:这是一个为函数提供运行环境的平台,包括认证服务、云数据库和HTTP网关等功能。
-
函数A、B、C:这些都是开发者发布的具体函数,每个函数都可以有自己的功能和用途。
-
HTTP网关:这是一个对外接口,允许外部客户端通过HTTP协议调用函数。
整个流程如下:开发者首先在本地或云端环境中开发函数,然后将其上传到函数仓库。之后,这些函数可以通过不同的触发器类型被触发,并在云函数上执行。执行结果可以通过函数运行平台的API返回给调用者。
鸿蒙端云一体化的这种架构使得开发者可以更专注于函数的开发,而无需关心底层的基础设施。同时,它也提供了高度的可扩展性和灵活性,使应用能够轻松应对各种业务需求的变化。
二、云函数开发准备
1、创建项目(如果已创建直接进入第二步)
1)登录,点击“我的项目”。
AGC平台地址
如果大家第一次点击我的项目,会出现以下同意协议,后续出现类似的,同意或者确定。
2)在项目页面中点击“添加项目”。
3)在“创建项目”页面中输入项目名称后 ,点击“创建并继续”。
说明:
点击“创建并继续”后,如果系统提示“您所在团队创建的项目数已经达到上限,请清理不需要的项目”,请进入“我的项目”,点击需要删除的项目卡片,点击“项目设置”页面下方的“删除项目”清理多余的项目。
2、添加应用(已经添加直接进入第三步)
如果已经创建了项目,可以使用已经创建的项目,如果不想使用已经创建的项目,则可以自己重新按照上一个小节创建AGC项目
-
若项目中没有应用,在“项目设置”页面中点击“添加应用”。
若项目中已有应用,展开顶部应用列表框,点击“添加应用”。
添加应用页面设置参数
第一步:选择APP(HarmonyOS)
第2步:选择APPID进行参数设置,点击下一步
下一步选择,项目,这里是默认的就好,因为我是通过项目来创建应用的,直接点击确认
说明:
如果提示“包名已存在,请更换包名”,您需要检查下您是否已经创建相同包名的应用,如果有,请勿重复创建。如果您未创建,请联系华为技术支持人员进行处理。
3、开通服务
- 登录AppGallery Connect,点击“我的项目”。
- 在项目列表中点击需要开通云函数的项目。
3. 在左侧导航栏选择“云开发(Serverless)> 云函数”,进入云函数页面,点击“立即开通”。
三、开发云函数(服务端编写+客户端调用)
1、案例01:简单计算案例
需求:实现用于计算两个数的值 之和的云函数
实现思路
1、服务器端创建云函数
2、服务器端测试云函数是否正常运行通过
3、编写客户端的代码来调用云函数
4、运行测试
实现步骤
第一步:服务器端创建云函数
新建一个函数名名为fun0901的函数名
点击创建
函数配置、点击下一步
触发器的选择,这里默认就够了
编辑函数代码
放大以后,编写代码,默认模版,我们可以在里面进行编辑,圈红的地方都不要动
编辑函数代码,函数的功能就是获取两个数,然后再进行相加,然后将相加的结果返回,代码编辑好了以后,缩小编辑区域,进入下一步
第二步:测试
完成以后,点击测试,运行函数
第三步:客户端调用
(1)创建项目
注意bundle name要和云侧项目的包名一致
(2)下载sdk配置文件 agconnect-services.json
将下载的json文件复制到src/main/resources/rawfile目录中
(3)配置oh-package.json5
entry\oh-package.json5
鸿蒙操作系统中的oh-package.json5
文件是用于配置鸿蒙应用的元数据文件,类似于Node.js中的package.json
。这个文件定义了鸿蒙应用的名称、版本、描述、主要入口文件、作者、许可证以及依赖关系等信息。json5
是JSON的一个扩展,它允许在文件中使用注释和更为宽松的语法。记得点sync
"dependencies": {
"@hw-agconnect/crypto-ohos": "^1.0.10",
"@hw-agconnect/function-ohos": "^1.0.10",
"@hw-agconnect/auth-ohos": "^1.0.10",
"@hw-agconnect/cloudstorage-ohos": "^1.0.10",
"@hw-agconnect/api-ohos": "^1.0.10",
"@hw-agconnect/base-ohos": "^1.0.10",
"@hw-agconnect/core-ohos": "^1.0.10",
"@hw-agconnect/credential-ohos": "^1.0.10",
"@ohos/agconnect-auth-component": "^1.0.5",
"@hw-agconnect/auth-component": "^1.0.0",
"@hw-agconnect/cloud": "^1.0.0",
"@hw-agconnect/hmcore": "^1.0.0",
"long": "5.2.1"
}
(4)加入网络权限
src/main/module.json5
"requestPermissions": [
{"name": 'ohos.permission.INTERNET'}
],
(5)修改.so文件的加载顺序
entry/build-profile.json5
...
"buildOption": {
//配置筛选har依赖.so资源文件的过滤规则
"napiLibFilterOption": {
//按照.so文件的优先级顺序,打包最高优先级的.so文件
"pickFirsts": [
"**/1.so"
],
//按照.so文件的优先级顺序,打包最低优先级的.so 文件
"pickLasts": [
"**/2.so"
],
//排除的.so文件
"excludes": [
"**/3.so"
],
//允许当.so重名冲突时,使用高优先级的.so文件覆盖低优先级的.so文件
"enableOverride": true,
}
}
...
(6)entryAbility初始化
import { initialize } from '@hw-agconnect/hmcore';
import json from '../../resources/rawfile/agconnect-services.json'
...
async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
try {
await initialize(this.context, json);
console.info('hmlog-->','AGConnectSuccessed!')
} catch (e) {
console.error('hmlog-->','AGConnectError', JSON.stringify(e))
}
}
(7) 客户端调用
import cloud from '@hw-agconnect/cloud'
import { FunctionResult } from '@hw-agconnect/function-ohos'
@Entry
@Component
struct Demo01 {
@State a:number=0 //加数1
@State b:number=0 //加数2
@State c:number=0 //结果
build() {
Column({space:30}){
Text("客户端调用云函数(案例一:简单案例)")
Row({space:15}){
TextInput({text:""+this.a}).width(80).onChange(val=>this.a=parseInt(val))
Text("+")
TextInput({text:""+this.b}).width(80).onChange(val=>this.b=parseInt(val))
Text("=")
Text(this.c+"")
}.width("100%")
Button("计算").width("60%").onClick(()=>{
this.callFun()
})
}
.width("100%")
.padding(30)
}
//点击计算按钮的的时候调用云函数
async callFun(){
//一般情况下肯定是需要看看输入是否为空,格式对不对
//以上我们假设做过了
//调用云函数
//函数名
//version 函数的版本 默认版本就是$latest
let res:FunctionResult=await cloud.callFunction({
name:"fun0901",
version:'$latest',
params:{
"num1":this.a,
"num2":this.b
}
})
// this.c = res.getValue()
// console.log(JSON.stringify(res.getValue())) //{"result":121}
this.c = res.getValue().result
console.log(this.c+"<====")
}
}
2、案例02:根据输入的年,获取这个年的生肖
实现思路
1、服务器端创建云函数
2、服务器端测试云函数是否正常运行通过
3、编写客户端的代码来调用云函数
4、运行测试
实现步骤
第一步:服务器端创建云函数
新建一个函数名名为fun0902的函数名,其他操作和上面案例01一致(参考上面),函数内容如下
// handler.js is a demo for handler function.
let myHandler = function(event, context, callback, logger) {
let res = new context.HTTPResponse({"simple": "example"}, {
"res-type": "simple example",
"faas-content-type": "json"
}, "application/json", "200");
//声明一个变量year,用于接受传递过来的参数
let year;
//如果body中有数据,那就是客户端调用传递过来的参数
if(event.body){
//获取传递过来的参数
year = JSON.parse(event.body).year
}else{
//如果是服务端调用当前的函数,那就直接通过event.year获取
year = event.year
}
//将year对12求余
//
function isNumber(input){
logger.info(parseInt(input).toString())
if(parseInt(input).toString()=='NaN'){
return false
}else{
return true
}
}
//自己封装的方法
function animal(inputyear){
let shengxiao ="" //这个变量用于保存具体的生效
if(!isNumber(inputyear)){
shengxiao="please input a number"
}else{
let yu=year%12
switch(yu){
case 0:
shengxiao='猴'
break
case 1:
shengxiao='鸡'
break
case 2:
shengxiao='狗'
break
case 3:
shengxiao='猪'
break
case 4:
shengxiao='鼠'
break
case 5:
shengxiao='牛'
break
case 6:
shengxiao='虎'
break
case 7:
shengxiao='兔'
break
case 8:
shengxiao='龙'
break
case 9:
shengxiao='蛇'
break
case 10:
shengxiao='马'
break
case 11:
shengxiao='羊'
break
}
}
return shengxiao;
}
//给调用者响应
res.body = {result:animal(year)}
// send response
callback(res);
};
module.exports.myHandler = myHandler;
客户端调用
import cloud from '@hw-agconnect/cloud'
@Entry
@Component
struct Demo02 {
//用于存放年
@State num:number=0
@State shengxiao:string = "" //用于查询生效的结果
build() {
Column({space:15}){
Text("客户端调用云函数(案例二:复杂点的示例)")
TextInput().onChange(val=>this.num=Number(val))
Button("查询生效").width("100%").onClick(()=>{
this.getShengXiao()
})
Text("生肖:"+this.shengxiao)
}
.width("100%")
.height("100%")
.padding(30)
}
async getShengXiao(){
try {
let rs = await cloud.callFunction({
name:"fun0902",
version:"$latest",
params:{
year:this.num
}
})
this.shengxiao = rs.getValue().result
}catch (e) {
console.log("====>error:",JSON.stringify(e))
}
}
}
四、开发云函数(使用端云一体化的方式)
案例01:简单的计算案例
1、创建项目、选择CloudDev,然后点击下一步
云侧项目,找到云函数(cloudfunctions)右击,new Cloud Function
创建好了的默认内容
右击函数,执行run
dev中,将函数测试好了没有问题,后续再传到云端
到此云侧函数创建好了,但是还没有实现具体的功能,要实现具体的功能,写代码调试,最后再上传到 云侧就可以
在dev中书写的云侧的代码,并测试成功
let myHandler = async function (event, context, callback, logger) {
logger.info(event);
// do something here
let num1:number = 0
let num2:number = 0
//如果是客户端调用
if(event.body){
let _body = JSON.parse(event.body)
num1 = _body.num1
num2 = _body.num2
}else{
//如果是服务器端调用就执行这个
num1 = event.num1
num2 = event.num2
}
let sum = num1+num2
callback({
code: 0,
desc: "Success.",
body:{
result:sum
}
});
};
export { myHandler };
下一步就将我们的在一体化项目中创建的云函数发布的云服务上去
上传到云函数成功了。
客户端,调用云函数
客户端可以使用这个端云一体化的项目(端侧)调用云函数,端侧项目没有rawfile目录,需要自己创建。另外还需配置依赖so文件加载顺序。。。。都需要再做
节约时间,我个人直接使用原来的不是端云一体化的项目进行访问了,这个功能和前面一样。
import cloud from '@hw-agconnect/cloud'
import { FunctionResult } from '@hw-agconnect/function-ohos'
@Entry
@Component
struct Demo03 {
@State a:number=0 //加数1
@State b:number=0 //加数2
@State c:number=0 //结果
build() {
Column({space:30}){
Text("客户端调用云函数(端云一体化创建的云函数)")
Row({space:15}){
TextInput({text:""+this.a}).width(80).onChange(val=>{
this.a=Number(val) //针对""转换为0 但是parseInt 针对空字符转成NaN
})
Text("+")
TextInput({text:""+this.b}).width(80).onChange(val=>this.b=Number(val))
Text("=")
Text(this.c+"")
}.width("100%")
Button("计算").width("60%").onClick(()=>{
this.callFun()
})
}
.width("100%")
.padding(30)
}
//点击计算按钮的的时候调用云函数
async callFun(){
//一般情况下肯定是需要看看输入是否为空,格式对不对
//以上我们假设做过了
//调用云函数
//函数名
//version 函数的版本 默认版本就是$latest
let res:FunctionResult=await cloud.callFunction({
name:"fun0903",
version:'$latest',
params:{
"num1":this.a,
"num2":this.b
}
})
// this.c = res.getValue()
// console.log(JSON.stringify(res.getValue())) //{"result":121}
console.log("====>",JSON.stringify(res.getValue())) //注意端云一体化云函数返回的内容
this.c = res.getValue().body.result
}
}
案例02:输入数值获取生效案例
1、云开发本地开发云函数
let myHandler = async function (event, context, callback, logger) {
/**
* 根据输入的一个年份 获取生效
* @param input 输入的年份
*/
function getZodiac(input){
let mo:number = input%12
let zodiac=""
switch (mo){
case 0:
zodiac="猴"
break
case 1:
zodiac="鸡"
break
case 2:
zodiac="狗"
break
case 3:
zodiac="猪"
break
case 4:
zodiac="鼠"
break
case 5:
zodiac="牛"
break
case 6:
zodiac="虎"
break
case 7:
zodiac="兔"
break
case 8:
zodiac="龙"
break
case 9:
zodiac="蛇"
break
case 10:
zodiac="马"
break
case 11:
zodiac="羊"
break
default :
zodiac="请输入数值"
break
}
return zodiac
}
let year
if(event.body){
year=JSON.parse(event.body).year
}else{
year=event.year
}
//如果year的类型是字符串类型的话 返回请输入数字
if(typeof(year)=='string'){
callback({
result:"对不起请输入数字"
});
}else{
//否则就正常返回生肖的内容
callback({
result:getZodiac(year)
})
}
};
export { myHandler };
2、端侧调用发布到云侧的代码
import cloud from '@hw-agconnect/cloud'
@Entry
@Component
struct Demo04 {
//用于存放年
@State num:number=0
@State shengxiao:string = "" //用于查询生效的结果
build() {
Column({space:15}){
Text("客户端调用云函数2(端云一体化上传的)")
TextInput().onChange(val=>{
if(Number(val).toString()=="NaN"){
this.num = -1
}else{
this.num = Number(val)
}
})
Button("查询生效").width("100%").onClick(()=>{
this.getShengXiao()
})
Text("生肖:"+this.shengxiao)
}
.width("100%")
.height("100%")
.padding(30)
}
async getShengXiao(){
try {
let rs = await cloud.callFunction({
name:"fun0904",
version:"$latest",
params:{
year:this.num
}
})
this.shengxiao = rs.getValue().result
}catch (e) {
console.log("====>error:",JSON.stringify(e))
}
}
}