AutoScaling 与函数计算结合,赋予更丰富的弹性能力

目前,弹性伸缩服务已经接入了负载均衡(SLB)、云数据库RDS 等云产品,但是暂未接入 云数据库Redis,有时候我们可能会需要弹性伸缩服务在扩缩容的时候自动将扩缩容涉及到的 ECS 实例私网 IP 添加到 Redis 白名单或者从 Redis 白名单中移除。本文将给出上述场景的最佳实践,向您介绍如何通过 AutoSclaing -> LifecycleHook -> MNS -> FC 的方式实现伸缩组发生扩容时自动将扩容出来的 ECS 实例私网 IP 添加到 Redis 白名单中,您可以在此基础上,根据您的业务需求进行扩展。

函数计算(FC)简介

阿里云函数计算是事件驱动的全托管计算服务。通过函数计算,您无需管理服务器等基础设施,只需编写代码并上传。函数计算会为您准备好计算资源,以弹性、可靠的方式运行您的代码,并提供日志查询、性能监控、报警等功能。借助于函数计算,您可以快速构建任何类型的应用和服务,无需管理和运维。而且,您只需要为代码实际运行所消耗的资源付费,代码未运行则不产生费用。更多关于函数计算的相关信息,您可以通过 函数计算官方文档 进行了解。

消息服务(MNS)简介

阿里云消息服务(Message Service,简称 MNS)是一种高效、可靠、安全、便捷、可弹性扩展的分布式消息服务。MNS能够帮助应用开发者在他们应用的分布式组件上自由的传递数据、通知消息,构建松耦合系统。更多关于消息服务的相关信息,您可以通过 消息服务官方文档 进行了解。

最佳实践

前提条件

在进行以下操作前,您需要先开通 函数计算服务FC 、 消息服务MNS 、弹性伸缩服务AutoScaling,接下来配置我们需要用的 FC、MNS、AutoScaling 相关信息

配置 MNS

登录 MNS控制台,创建 MNS 主题(作为函数计算的触发器),如下图所示:

image

同样的,创建 MNS 队列,MNS 队列作为函数计算执行结果接收器,队列名称会在代码中进行配置。

配置 FC

登录FC控制台,新建服务,如下图所示:

image

服务创建好以后,新增函数,如下图所示:

image

点击新增函数,弹出新建函数对话框,如下图所示:

image

选择函数语言,并选择空白模板,跳转到触发器配置界面,如下图所示:

image

配置好触发器类型、触发器名称以及对应的 MNS 主题(MNS 主题与 FC 所属的地域最好相同),点击下一步,跳转到基础管理配置界面,如下图所示:

image
image

所在服务默认会选择当前服务,不用改变,填写函数名称,选择运行环境,通过代码包上传的方式上传提前测试好的 java jar包(即触发函数计算时需要执行的运行的程序,本文最后会给出示例jar包),按照说明填写好函数入口,点击下一步,跳转到模版授权管理界面,如下图所示:

image
image

首先授予函数运行所需要的权限,授权时候应遵循权限最小化原则,防止权限过大,如上图步骤1、2所示,再授予 MNS 触发 FC 所需的权限,如上图步骤3、4所示,最后点击下一步,跳转到信息核对界面,如下图所示:

image

核对信息无误,点击创建,函数创建完成。

关于函数计算的配置过程,您可以通过 FC Hello World示例 进行了解。

创建云数据库 Redis

登录 Redis控制台,选择和 MNS 、FC 相同的地域,创建 Redis 实例。实例创建完以后,查看实例的白名单设置,如下图所示:
image

配置 AutoScaling

登录 弹性伸缩控制台,创建好伸缩组以及伸缩配置以后,创建生命周期挂钩(LifecycleHook),如下图所示:

image

上图中,在左侧导航栏选择生命周期挂钩,点击创建生命周期挂钩按钮,填写名称,选择生命周期挂钩对应的伸缩活动类型,配置生命周期挂钩对应的 MNS 通知为 MNS 主题,并且选择的主题为 FC 触发器对应的主题,最后点击创建按钮,生命周期挂钩函数创建完成,如下图所示:

image

在伸缩组发生扩容伸缩活动时,实例创建完成并运行起来以后,生命周期挂钩会被触发,并发送伸缩活动相关信息到生命周期挂钩配置的 MNS 主题上,挂起当前的伸缩活动,直到生命周期挂钩超时或者被提前结束。生命周期挂钩活动结束以后,伸缩活动继续执行,扩容出来的 ECS 实例会被挂载到负载均衡实例上(如果伸缩组配置了负载均衡实例的话)。关于生命周期挂钩功能的详细说明,您可以通过云栖博客 AutoScaling 生命周期挂钩功能 进行详细了解。

触发扩容伸缩活动

首先,我们通过触发扩容伸缩活动的方式,创建 10 台 ECS 实例,对应的伸缩活动如下图所示:

image

然后我们登录 MNS控制台,查看队列接收到的 FC 执行结果消息,如下图所示:

image

上述消息中 success 为 true,表示函数计算执行成功(即 ECS 实例私网 IP 添加到 Redis 白名单成功),消息体中还包括了当前生命周期挂钩活动对应的 LifecycleHookId LifecycleActionToken 参数信息,您可以根据相关参数信息调用 CompleteLifecycleAction 接口提前结束生命周期活动。

最后,我们登录 云数据库Redis控制台,查看当前的 Redis 白名单信息,如下图所示:

image

从上图可以看出,弹性伸缩扩容活动创建出来的 ECS 实例私网 IP 成功添加到 Redis 白名单中。

至此,通过 AutoScaling -> LifecycleHook -> MNS -> FC 实现 Redis 白名单自动添加的过程结束,整体过程如下:

  1. 弹性伸缩组触发扩容伸缩活动,扩容 ECS 实例,扩容活动触发生命周期挂钩
  2. 生命周期挂钩将扩容活动挂起,同时发送消息到 MNS 主题
  3. MNS 主题接收到消息以后将消息作为输入信息触发 FC,FC 被触发以后执行预置业的 JAVA 函数
  4. JAVA 函数获取 FC 触发器的输入信息,信息中包括了本次伸缩活动对应的 ECS 实例 ID信息,通过接口获取 ECS 实例私网 IP 以后添加到 Redis default 分组白名单中
  5. 最后,函数执行结果发送到代码中配置好的 MNS 队列中

上述过程仅作为一个参考的 Demo,进一步实现自动化管理,还需要我们自己编程实现,如编程的方式消费 MNS 队列中的消息,获取执行结果与 LifecycleHookId LifecycleActionToken等参数信息提前结束生命周期挂钩活动等。

FC 预置 JAVA 代码解析

FC 预置函数为 JAVA 代码,通过 Maven 管理,对应的代码及依赖如下:

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";@Overridepublic 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<MnsMessageModel>() {});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);//获取本次伸缩活动对应实例的私网IPList<String> 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<String> 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<String> filterPrivateIpsForAppend(List<String> privateIps, IAcsClient client)throws ClientException {List<String> needAppendIps = new ArrayList<>();if (CollectionUtils.isEmpty(privateIps)) {return needAppendIps;}DescribeSecurityIpsRequest request = new DescribeSecurityIpsRequest();request.setInstanceId(REDIS_ID);DescribeSecurityIpsResponse response = client.getAcsResponse(request);List<DescribeSecurityIpsResponse.SecurityIpGroup> 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<String> 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<String> 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<String> getInstancesPrivateIps(List<String> instanceIds, IAcsClient client)throws Exception {List<String> 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 instancefor (int i = 1; i <= batchCount; i++) {int fromIndex = queryNumberPerTime * (i - 1);int toIndex = Math.min(queryNumberPerTime * i, size);List<String> subList = instanceIds.subList(fromIndex, toIndex);DescribeInstancesRequest request = new DescribeInstancesRequest();request.setInstanceIds(JSON.toJSONString(subList));DescribeInstancesResponse response = client.getAcsResponse(request);List<DescribeInstancesResponse.Instance> 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<String> privateIpAddress = vpcAttributes.getPrivateIpAddress();if (!CollectionUtils.isEmpty(privateIpAddress)) {privateIp = privateIpAddress.get(0);}}} else {List<String> innerIpAddress = instance.getInnerIpAddress();if (!CollectionUtils.isEmpty(innerIpAddress)) {privateIp = innerIpAddress.get(0);}}return privateIp;}
}

代码中涉及到的 Model 文件

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;}@Overridepublic 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<String>      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<String> getInstanceIds() {return instanceIds;}public void setInstanceIds(List<String> 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;}
}

Maven 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.aliyun.fc.wujin</groupId><artifactId>demo</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-ecs</artifactId><version>4.10.1</version></dependency><dependency><groupId>com.aliyun.fc.runtime</groupId><artifactId>fc-java-core</artifactId><version>1.0.0</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>3.2.6</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-r-kvstore</artifactId><version>2.0.3</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.25</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>4.2.5.RELEASE</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.2</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>com.springsource.org.apache.commons.lang</artifactId><version>2.6.0</version></dependency><dependency><groupId>com.aliyun.mns</groupId><artifactId>aliyun-sdk-mns</artifactId><version>1.1.8.4</version></dependency></dependencies><build><plugins><plugin><artifactId>maven-assembly-plugin</artifactId><version>3.1.0</version><configuration><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs><appendAssemblyId>false</appendAssemblyId> <!-- this is used for not append id to the jar name --></configuration><executions><execution><id>make-assembly</id> <!-- this is used for inheritance merges --><phase>package</phase> <!-- bind to the packaging phase --><goals><goal>single</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins></build></project>

上述java文件中,Example.java 文件在包名为 fc 的目录下,FCResult.java HookModel.java MnsMessageModel.java 三个文件在包名为 model 的目录下,package fc 与 package model 处于同级目录。
Example.java 文件需要根据实际情况对相关参数进行替换,QUEUE_NAME 参数定义了接收函数执行结果的 MNS 队列,我们在 配置 MNS 章节已经提前创建好了。
参数替换完成以后,可以参考 FC Java 编程说明 重新打包并上传您的 jar 包即可,上传方法如下图所示:
image

写在最后

通过 AutoScaling -> LifecycleHook -> MNS -> FC 的方式,您可以具备更加丰富的弹性能力,从而更加灵活地管理您伸缩组内的资源。

上述代码仅供参考,具体实现需要结合具体业务进行测试改造。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/388751.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

参考文献_参考

参考文献Recently, I am attracted by the news that Tanzania has attained lower middle income status under the World Bank’s classification, five years ahead of projection. Being curious on how they make the judgement, I take a look of the World Bank’s offi…

java语言静态分析工具_PMD 6.16.0 发布,跨语言静态代码自动分析工具

PMD 6.16.0 发布了。PMD 是一个代码分析器&#xff0c;能够帮助发现常见的编程问题&#xff0c;比如未使用的变量、空的 catch 块、不必要的对象创建等等。最初仅支持 Java 代码&#xff0c;目前还可支持 JavaScript、Salesforce.com Apex 和 Visualforce、PLSQL、Apache Veloc…

B1922 [Sdoi2010]大陆争霸 最短路

我一直都不会dij的堆优化&#xff0c;今天搞了一下。。。就是先弄一个优先队列&#xff0c;存每个点的数据&#xff0c;然后这个题就加了一点不一样的东西&#xff0c;每次的最短路算两次&#xff0c;一次是自己的最短路&#xff0c;另一次是机关的最短路&#xff0c;两者取最大…

WPF中的鼠标事件详解

WPF中的鼠标事件详解 Uielement和ContentElement都定义了十个以Mouse开头的事件&#xff0c;8个以PreviewMouse开头的事件&#xff0c;MouseMove,PreviewMouseMove,MouseEnter,Mouseleave的事件处理器类型都是MouseEventHandler类型。这些事件都具备对应得MouseEventargs对象。…

数据统计 测试方法_统计测试:了解如何为数据选择最佳测试!

数据统计 测试方法This post is not meant for seasoned statisticians. This is geared towards data scientists and machine learning (ML) learners & practitioners, who like me, do not come from a statistical background.Ť他的职位是不是意味着经验丰富的统计人…

前端介绍-35

前端介绍-35 # 前端## 一、什么是前端 前端即网站前台部分&#xff0c;运行在PC端&#xff0c;移动端等浏览器上展现给用户浏览的网页。随着互联网技术的发展&#xff0c;HTML5&#xff0c;CSS3&#xff0c;前端框架的应用&#xff0c;跨平台响应式网页设计能够适应各种屏幕…

spring的几个通知(前置、后置、环绕、异常、最终)

1、没有异常的 2、有异常的 1、被代理类接口Person.java 1 package com.xiaostudy;2 3 /**4 * desc 被代理类接口5 * 6 * author xiaostudy7 *8 */9 public interface Person { 10 11 public void add(); 12 public void update(); 13 public void delete();…

每个Power BI开发人员的Power Query提示

If someone asks you to define the Power Query, what should you say? If you’ve ever worked with Power BI, there is no chance that you haven’t used Power Query, even if you weren’t aware of it. Therefore, one could easily say that Power Query is the “he…

c# PDF 转换成图片

1.新建项目 2.新增一个新文件夹“lib”&#xff08;主要是为了存放引用的dll&#xff09; 3.将“gsdll32.dll 、PDFLibNet.dll 、PDFView.dll”3个dll添加到文件夹中 4.项目添加“PDFLibNet.dll 、PDFView.dll”2个类库的引用&#xff0c;并将gsdll32.dll 拷贝到项目生产根…

java finally在return_Java finally语句到底是在return之前还是之后执行?

点击上方“方志朋”&#xff0c;选择“置顶或者星标”你的关注意义重大&#xff01;网上有很多人探讨Java中异常捕获机制try...catch...finally块中的finally语句是不是一定会被执行&#xff1f;很多人都说不是&#xff0c;当然他们的回答是正确的&#xff0c;经过我试验&#…

oracle 死锁

为什么80%的码农都做不了架构师&#xff1f;>>> ORA-01013: user requested cancel of current operation 转载于:https://my.oschina.net/8808/blog/2994537

面试题:二叉树的深度

题目描述&#xff1a;输入一棵二叉树&#xff0c;求该树的深度。从根结点到叶结点依次经过的结点&#xff08;含根、叶结点&#xff09;形成树的一条路径&#xff0c;最长路径的长度为树的深度。 思路&#xff1a;递归 //递归 public class Solution {public int TreeDepth(Tre…

a/b测试_如何进行A / B测试?

a/b测试The idea of A/B testing is to present different content to different variants (user groups), gather their reactions and user behaviour and use the results to build product or marketing strategies in the future.A / B测试的想法是将不同的内容呈现给不同…

hibernate h2变mysql_struts2-hibernate-mysql开发案例 -解道Jdon

Hibernate专题struts2-hibernate-mysql开发案例与源码源码下载本案例展示使用Struts2&#xff0c;Hibernate和MySQL数据库开发一个个人音乐管理器Web应用程序。&#xff0c;可将您的音乐收藏添加到数据库中。功能有&#xff1a;显示一个添加记录的表单和所有的音乐收藏的列表。…

P5024 保卫王国

传送门 我现在还是不明白为什么NOIPd2t3会是一道动态dp…… 首先关于动态dp可以看这里 然后这里就是把把矩阵给改一改&#xff0c;改成这个形式\[\left[dp_{i-1,0},dp_{i-1,1}\right]\times \left[\begin{matrix}\infty&ldp_{i,1}\\ldp_{i,0}&ldp_{i,1}\end{matrix}\ri…

提取图像感兴趣区域_从图像中提取感兴趣区域

提取图像感兴趣区域Welcome to the second post in this series where we talk about extracting regions of interest (ROI) from images using OpenCV and Python.欢迎来到本系列的第二篇文章&#xff0c;我们讨论使用OpenCV和Python从图像中提取感兴趣区域(ROI)。 As a rec…

解决java compiler level does not match the version of the installed java project facet

ava compiler level does not match the version of the installed java project facet错误的解决 因工作的关系&#xff0c;Eclipse开发的Java项目拷来拷去&#xff0c;有时候会报一个很奇怪的错误。明明源码一模一样&#xff0c;为什么项目复制到另一台机器上&#xff0c;就会…

php模板如何使用,ThinkPHP如何使用模板

到目前为止&#xff0c;我们只是使用了控制器和模型&#xff0c;还没有接触视图&#xff0c;下面来给上面的应用添加视图模板。首先我们修改下 Action 的 index 操作方法&#xff0c;添加模板赋值和渲染模板操作。PHP代码classIndexActionextendsAction{publicfunctionindex(){…

理解Windows窗体和WPF中的跨线程调用

你曾开发过Windows窗体程序&#xff0c;可能会注意到有时事件处理程序将抛出InvalidOperationException异常&#xff0c;信息为“ 跨线程调用非法&#xff1a;在非创建控件的线程上访问该控件”。这种Windows窗体应用程序中 跨线程调用时的一个最为奇怪的行为就是&#xff0c;有…

什么是嵌入式系统

在我们的日常生活中&#xff0c;我们经常使用许多使用嵌入式系统技术设计的电气和电子电路和套件。计算机&#xff0c;手机&#xff0c;平板&#xff0c;笔记本电脑&#xff0c;数字电子系统以及其他电子和电子设备都是使用嵌入式系统设计的。 什么是嵌入式系统&#xff1f;将硬…