mysql函数 returns obj_数据库函数return

本文介绍如何使用弹性伸缩、消息服务和函数计算,将弹性扩张时创建的ECS实例自动添加到Redis实例的白名单。

前提条件 使用本教程进行操作前,请确保您已经注册了阿里云账号。如还未注册,请先完成账号注册。 已经开通云数据库Redis、消息服务、弹性伸缩和函数计算。 背景信息 使用弹性伸缩时,伸缩组可以关联负载均衡实例和云数据库RDS实例,但是暂时不能关联云数据库Redis实例。如果您将业务数据存储在Redis实例上,会需要配置伸缩组内的ECS实例加入Redis实例的访问白名单。等待ECS实例创建完成后再逐个手动添加至Redis实例白名单不仅费时费力,也容易出现失误,在维护大量实例时成本较高。

针对这种情况,您可以在伸缩组中创建生命周期挂钩,生命周期挂钩在弹性扩张时会自动向指定的MNS主题发送消息,然后通过函数计算中的MNS主题触发器,触发执行上传的代码,自动将ECS实例添加到Redis实例的白名单。 说明 请在相同地域创建消息服务主题和函数计算服务,相同区域内的产品内网可以互通。 自动化管理实践-添加ECS实例到Redis白名单流程 本文以Java语言的形式给出示例代码,您可以根据业务需求,将此类最佳实践扩展到其它语言。

操作步骤 执行以下操作自动将伸缩组ECS实例添加到Redis实例白名单: 步骤一:创建Redis实例 步骤二:创建MNS主题和MNS队列 步骤三:创建伸缩组和生命周期挂钩 步骤四:创建服务和函数 步骤一:创建Redis实例 登录云数据库Redis控制台。 创建一台Redis实例。 具体操作请参见创建Redis实例,用于为自动创建的ECS实例提供数据库服务。 查看Redis实例的白名单,确定执行代码前的白名单状态。 自动化管理实践-查看Redis实例白名单 步骤二:创建MNS主题和MNS队列 登录消息服务控制台。 创建一个MNS主题。 用作执行函数的触发器,本示例主题的名称为fc-trigger。自动化管理实践-创建MNS主题消息 创建一个MNS队列。 用作函数执行结果的接收器,本示例队列的名称为fc-callback。示例代码中通过QUEUE_NAME指定该队列,发送包含函数执行结果的消息。自动化管理实践-创建MNS队列消息 步骤三:创建伸缩组和生命周期挂钩 登录弹性伸缩控制台。 创建一个伸缩组。 具体操作请参见创建伸缩组或者使用实例启动模板创建伸缩组。 创建一个生命周期挂钩。 具体操作请参见创建生命周期挂钩。 适用的伸缩活动类型配置为弹性扩张活动,用于通知弹性扩张事件。 通知方式配置为MNS主题,与MNS队列相比,主题可以通知多个订阅者,执行多种操作。 MNS主题配置为fc-trigger,用于在自动创建的ECS实例进入加入挂起中状态时执行代码,将ECS实例添加到云数据库Redis的白名单。 根据需要配置其它选项。 步骤四:创建服务和函数 登录函数计算控制台。 新建一个服务。 具体操作请参见创建服务,用于承载需要执行的函数,本示例服务的名称为as-hook-mns-fc-redis。自动化管理实践-创建函数计算服务 在服务下新建函数,订阅MNS主题并上传代码。 具体操作请参见新建函数。 在函数模板页面中,选择空白函数。 在触发器配置页面中,选择MNS 主题触发器,然后根据需要配置其它选项。 自动化管理实践-配置函数触发器 在基础管理配置页面中,所在服务配置为as-hook-mns-fc-redis,函数入口配置为fc.Example::handleRequest,然后根据需要配置其它选项。 函数入口由代码决定,请根据实际情况配置。 本文示例采用上传jar包,实现将自动创建的ECS实例添加到云数据库Redis的白名单。有关编程语言说明,请参见函数计算Java编程说明。 自动化管理实践-配置函数基础信息自动化管理实践-配置函数基础信息-函数入口 在权限配置页面中,根据需要授予函数访问其它资源的权限,并授予消息服务调用函数的权限。 说明 建议遵循权限最小化原则,仅授予必需的权限,防范潜在风险。 自动化管理实践-配置权限-函数计算操作其它资源自动化管理实践-配置权限-调用函数 在信息核对页面中,核对函数信息和触发器信息,然后单击创建。 执行效果 配置完成后,执行效果如下:

在满足弹性扩张的条件时,伸缩组触发伸缩活动,自动创建ECS实例。 生命周期挂钩挂起伸缩活动,同时发送消息到MNS主题。 函数计算中,MNS主题触发器触发函数执行过程,并将消息内容作为输入信息(包括ECS实例的ID等信息),执行Java代码。 代码执行时,会通过接口获取ECS实例的私网IP,然后将私网IP添加到Redis实例的白名单(default 分组)。 代码执行结果会发送到MNS队列fc-callback,您可以在消息服务控制台查看结果详情。查看消息内容中success为true,即表明ECS实例成功添加到了Redis实例的白名单。自动化管理实践-查看执行效果 您还可以继续消费MNS队列中的消息,比如获取success、LifecycleHookId和LifecycleActionToken,编程提前结束生命周期挂钩。

上述最佳实践供您参考,您也在其它场景下通过多款产品实现自动化管理,从而更加灵活地管理伸缩组内的资源。

示例代码 示例代码仅供参考,请结合具体业务进行测试改造。主要功能涉及四个java文件,通过Maven管理,目录结构如下:

自动化管理实践-Jar包结构 Maven依赖如下:

4.0.0

com.aliyun.fc.wujin

demo

1.0-SNAPSHOT

com.aliyun

aliyun-java-sdk-ecs

4.10.1

com.aliyun.fc.runtime

fc-java-core

1.0.0

com.aliyun

aliyun-java-sdk-core

3.2.6

com.aliyun

aliyun-java-sdk-r-kvstore

2.0.3

com.alibaba

fastjson

1.2.25

org.springframework

spring-context

4.2.5.RELEASE

org.apache.httpcomponents

httpclient

4.5.2

org.apache.commons

com.springsource.org.apache.commons.lang

2.6.0

com.aliyun.mns

aliyun-sdk-mns

1.1.8.4

maven-assembly-plugin

3.1.0

jar-with-dependencies

false

make-assembly

package

single

org.apache.maven.plugins

maven-compiler-plugin

1.8

1.8

Example.java代码如下:

package fc;

import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import com.aliyun.fc.runtime.Context; import com.aliyun.fc.runtime.StreamRequestHandler; import com.aliyun.mns.client.CloudAccount; import com.aliyun.mns.client.CloudQueue; import com.aliyun.mns.client.MNSClient; import com.aliyun.mns.model.Message; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.ecs.model.v20140526.DescribeInstancesRequest; import com.aliyuncs.ecs.model.v20140526.DescribeInstancesResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; import com.aliyuncs.r_kvstore.model.v20150101.DescribeSecurityIpsRequest; import com.aliyuncs.r_kvstore.model.v20150101.DescribeSecurityIpsResponse; import com.aliyuncs.r_kvstore.model.v20150101.ModifySecurityIpsRequest; import model.FCResult; import model.HookModel; import model.MnsMessageModel; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang.StringUtils; import org.springframework.util.CollectionUtils;

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class Example implements StreamRequestHandler {

/**

* 专有网络类型,此参数不用变

*/

private static final String VPC_NETWORK = "vpc";

private static final String CHAR_SET = "UTF-8";

/**

* 接收input数组大小,4096通常够用

*/

private static final Integer MAX_BYTE_LENGTH = 4096;

/**

* REDIS 白名单默认分组

*/

private static final String DEFAULT_SECURITY_GROUP_NAME = "default";

/**

* REDIS 修改白名单的模式

*/

private static final String MODIFY_MODE_APPEND = "Append";

/**

* MNS 客户端发送消息地址

*/

private static final String MNS_END_POINT = "http://%s.mns.%s.aliyuncs.com/";

/**

* 待添加的REDIS实例ID,根据个人情况替换

*/

private static final String REDIS_ID = "";

/**

* 接收本次函数计算执行结果的队列名称,根据个人情况替换

*/

private static final String QUEUE_NAME = "wujin-fc-callback";

/**

* 阿里云账号UID,根据跟人情况替换

*/

private static final Long USER_ID = 1111111111111111111L;

/**

* 伸缩组 MNS FC 所属的region,根据个人情况替换

*/

private static final String REGION_ID = "cn-hangzhou";

@Override

public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) {

FCResult result = new FCResult();

String akId = context.getExecutionCredentials().getAccessKeyId();

String akSecret = context.getExecutionCredentials().getAccessKeySecret();

String securityToken = context.getExecutionCredentials().getSecurityToken();

try {

//获取MNS触发函数计算时输入的内容

String input = readInput(inputStream);

MnsMessageModel mnsMessageModel = JSON.parseObject(input,

new TypeReference() {

});

if (mnsMessageModel == null) {

result.setSuccess(false);

result.setMessage("mnsMessageModel is null");

sendMns(akId, akSecret, securityToken, result.toString());

return;

}

HookModel contentModel = mnsMessageModel.getContent();

if (contentModel == null) {

result.setSuccess(false);

result.setMessage("contentModel is null");

sendMns(akId, akSecret, securityToken, result.toString());

return;

}

IAcsClient client = buildClient(akId, akSecret, securityToken);

//获取本次伸缩活动对应实例的私网IP

List privateIps = getInstancesPrivateIps(contentModel.getInstanceIds(), client);

if (CollectionUtils.isEmpty(privateIps)) {

result.setSuccess(false);

result.setMessage("privateIps is empty");

sendMns(akId, akSecret, securityToken, result.toString());

return;

}

List needAppendIps = filterPrivateIpsForAppend(privateIps, client);

if (!CollectionUtils.isEmpty(needAppendIps)) {

modifySecurityIps(client, needAppendIps);

result.setLifecycleHookId(contentModel.getLifecycleHookId());

result.setLifecycleActionToken(contentModel.getLifecycleActionToken());

sendMns(akId, akSecret, securityToken, result.toString());

}

} catch (Exception ex) {

result.setSuccess(false);

result.setMessage(ex.getMessage());

sendMns(akId, akSecret, securityToken, result.toString());

}

}

/**

* 构建请求 ECS Redis 接口客户端

*

* @param akId

* @param akSecret

* @param securityToken

* @return

*/

private IAcsClient buildClient(String akId, String akSecret, String securityToken) {

IClientProfile clientProfile = DefaultProfile.getProfile(REGION_ID, akId, akSecret,

securityToken);

return new DefaultAcsClient(clientProfile);

}

/**

* 将执行结果发送消息到MNS

*

* @param ak

* @param aks

* @param securityToken

* @param msg

*/

private void sendMns(String ak, String aks, String securityToken, String msg) {

MNSClient client = null;

try {

CloudAccount account = new CloudAccount(ak, aks,

String.format(MNS_END_POINT, USER_ID, REGION_ID), securityToken);

client = account.getMNSClient();

CloudQueue queue = client.getQueueRef(QUEUE_NAME);

Message message = new Message();

message.setMessageBody(msg);

queue.putMessage(message);

} finally {

if (client != null) {

client.close();

}

}

}

/**

* 过滤出需要添加到redis的私网IP

*

* @param privateIps 过滤以前的私网IP

* @param client

* @return

* @throws ClientException

*/

private List filterPrivateIpsForAppend(List privateIps, IAcsClient client)

throws ClientException {

List needAppendIps = new ArrayList<>();

if (CollectionUtils.isEmpty(privateIps)) {

return needAppendIps;

}

DescribeSecurityIpsRequest request = new DescribeSecurityIpsRequest();

request.setInstanceId(REDIS_ID);

DescribeSecurityIpsResponse response = client.getAcsResponse(request);

List securityIpGroups = response

.getSecurityIpGroups();

if (CollectionUtils.isEmpty(securityIpGroups)) {

return privateIps;

}

for (DescribeSecurityIpsResponse.SecurityIpGroup securityIpGroup : securityIpGroups) {

if (!securityIpGroup.getSecurityIpGroupName().equals(DEFAULT_SECURITY_GROUP_NAME)) {

continue;

}

String securityIps = securityIpGroup.getSecurityIpList();

if (securityIps == null) {

continue;

}

String[] securityIpList = securityIps.split(",");

List existIps = Arrays.asList(securityIpList);

if (CollectionUtils.isEmpty(existIps)) {

continue;

}

for (String ip : privateIps) {

if (!existIps.contains(ip)) {

needAppendIps.add(ip);

}

}

}

return privateIps;

}

/**

* 修改REDIS实例DEFAULT分组私网IP白名单

*

* @param client

* @param needAppendIps

* @throws ClientException

*/

private void modifySecurityIps(IAcsClient client, List needAppendIps)

throws ClientException {

if (CollectionUtils.isEmpty(needAppendIps)) {

return;

}

ModifySecurityIpsRequest request = new ModifySecurityIpsRequest();

request.setInstanceId(REDIS_ID);

String ip = StringUtils.join(needAppendIps.toArray(), ",");

request.setSecurityIps(ip);

request.setSecurityIpGroupName(DEFAULT_SECURITY_GROUP_NAME);

request.setModifyMode(MODIFY_MODE_APPEND);

client.getAcsResponse(request);

}

/**

* 获取输入,并base64解码

*

* @param inputStream

* @return

* @throws IOException

*/

private String readInput(InputStream inputStream) throws IOException {

try {

byte[] bytes = new byte[MAX_BYTE_LENGTH];

int tmp;

int len = 0;

//循环读取所有内容

while ((tmp = inputStream.read()) != -1 && len < MAX_BYTE_LENGTH) {

bytes[len] = (byte) tmp;

len++;

}

inputStream.close();

byte[] act = new byte[len];

System.arraycopy(bytes, 0, act, 0, len);

return new String(Base64.decodeBase64(act), CHAR_SET);

} finally {

inputStream.close();

}

}

/**

* 获取实例列表对应的私网IP,并限制每次请求实例数量不超过100

*

* @param instanceIds 实例列表

* @param client 请求客户端

* @return

* @throws Exception

*/

public List getInstancesPrivateIps(List instanceIds, IAcsClient client)

throws Exception {

List privateIps = new ArrayList<>();

if (CollectionUtils.isEmpty(instanceIds)) {

return privateIps;

}

int size = instanceIds.size();

int queryNumberPerTime = 100;

int batchCount = (int) Math.ceil((float) size / (float) queryNumberPerTime);

//support 100 instance

for (int i = 1; i <= batchCount; i++) {

int fromIndex = queryNumberPerTime * (i - 1);

int toIndex = Math.min(queryNumberPerTime * i, size);

List subList = instanceIds.subList(fromIndex, toIndex);

DescribeInstancesRequest request = new DescribeInstancesRequest();

request.setInstanceIds(JSON.toJSONString(subList));

DescribeInstancesResponse response = client.getAcsResponse(request);

List instances = response.getInstances();

if (CollectionUtils.isEmpty(instances)) {

continue;

}

for (DescribeInstancesResponse.Instance instance : instances) {

String privateIp = getPrivateIp(instance);

if (privateIp != null) {

privateIps.add(privateIp);

}

}

}

return privateIps;

}

/**

* 从 DescribeInstancesResponse.Instance 中解析出私网 IP

*

* @param instance DescribeInstancesResponse.Instance

*/

private String getPrivateIp(DescribeInstancesResponse.Instance instance) {

String privateIp = null;

if (VPC_NETWORK.equalsIgnoreCase(instance.getInstanceNetworkType())) {

DescribeInstancesResponse.Instance.VpcAttributes vpcAttributes = instance

.getVpcAttributes();

if (vpcAttributes != null) {

List privateIpAddress = vpcAttributes.getPrivateIpAddress();

if (!CollectionUtils.isEmpty(privateIpAddress)) {

privateIp = privateIpAddress.get(0);

}

}

} else {

List innerIpAddress = instance.getInnerIpAddress();

if (!CollectionUtils.isEmpty(innerIpAddress)) {

privateIp = innerIpAddress.get(0);

}

}

return privateIp;

}

} FCResult.java代码如下:

package model;

import com.alibaba.fastjson.JSON;

public class FCResult {

private boolean success = true;

private String lifecycleHookId;

private String lifecycleActionToken;

private String message;

public boolean isSuccess() { return success; }

public void setSuccess(boolean success) { this.success = success; }

public String getLifecycleHookId() { return lifecycleHookId; }

public void setLifecycleHookId(String lifecycleHookId) { this.lifecycleHookId = lifecycleHookId; }

public String getLifecycleActionToken() { return lifecycleActionToken; }

public void setLifecycleActionToken(String lifecycleActionToken) { this.lifecycleActionToken = lifecycleActionToken; }

public String getMessage() { return message; }

public void setMessage(String message) { this.message = message; }

@Override public String toString() { return JSON.toJSONString(this); } } HookModel.java代码如下:

package model;

import java.util.List;

public class HookModel {

private String lifecycleHookId;

private String lifecycleActionToken;

private String lifecycleHookName;

private String scalingGroupId;

private String scalingGroupName;

private String lifecycleTransition;

private String defaultResult;

private String requestId;

private String scalingActivityId;

private List instanceIds;

public String getLifecycleHookId() {

return lifecycleHookId;

}

public void setLifecycleHookId(String lifecycleHookId) {

this.lifecycleHookId = lifecycleHookId;

}

public String getLifecycleActionToken() {

return lifecycleActionToken;

}

public void setLifecycleActionToken(String lifecycleActionToken) {

this.lifecycleActionToken = lifecycleActionToken;

}

public String getLifecycleHookName() {

return lifecycleHookName;

}

public void setLifecycleHookName(String lifecycleHookName) {

this.lifecycleHookName = lifecycleHookName;

}

public String getScalingGroupId() {

return scalingGroupId;

}

public void setScalingGroupId(String scalingGroupId) {

this.scalingGroupId = scalingGroupId;

}

public String getScalingGroupName() {

return scalingGroupName;

}

public void setScalingGroupName(String scalingGroupName) {

this.scalingGroupName = scalingGroupName;

}

public String getLifecycleTransition() {

return lifecycleTransition;

}

public void setLifecycleTransition(String lifecycleTransition) {

this.lifecycleTransition = lifecycleTransition;

}

public String getDefaultResult() {

return defaultResult;

}

public void setDefaultResult(String defaultResult) {

this.defaultResult = defaultResult;

}

public String getRequestId() {

return requestId;

}

public void setRequestId(String requestId) {

this.requestId = requestId;

}

public String getScalingActivityId() {

return scalingActivityId;

}

public void setScalingActivityId(String scalingActivityId) {

this.scalingActivityId = scalingActivityId;

}

public List getInstanceIds() {

return instanceIds;

}

public void setInstanceIds(List instanceIds) {

this.instanceIds = instanceIds;

}

} MnsMessageModel.java代码如下:

package model;

public class MnsMessageModel {

private String userId;

private String regionId;

private String resourceArn;

private HookModel content;

public String getUserId() {

return userId;

}

public void setUserId(String userId) {

this.userId = userId;

}

public String getRegionId() {

return regionId;

}

public void setRegionId(String regionId) {

this.regionId = regionId;

}

public String getResourceArn() {

return resourceArn;

}

public void setResourceArn(String resourceArn) {

this.resourceArn = resourceArn;

}

public HookModel getContent() {

return content;

}

public void setContent(HookModel content) {

this.content = content;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值