• 关于

    函数计算活动地址

    的搜索结果

回答

事件请求场景 定制图片 网店店家进行商品图片维护时,需要根据商品陈列位置,将图片动态切割成不同尺寸,或者打上不同水印。当店家把图片上传到对象存储 OSS上,会通过函数计算上定制的trigger来触发函数计算。根据计算规则,生成不同尺寸的图片,满足在线商品陈列需求,整个过程无需再搭建额外服务器,也无需网站美工干预。 物联网中的低频请求 物联网行业中,物联网设备传输数据量小,且往往是以固定时间间隔进行数据传输,因此经常涉及低频请求场景。例如:物联网应用程序每分钟仅运行一次,每次运行 50ms,这意味着CPU的使用率仅为 0.1%/小时,或者说有 1000 个相同的应用可以共享计算资源。而Serverless架构下,用户可以购买每分钟 100ms 的资源来满足计算需求,既能有效解决效率问题,也能降低使用成本。 定制事件 用户注册时发邮件验证邮箱地址,同样可以通过定制的事件来触发后续的注册流程,而无需再配置额外的应用无服务器来处理后续的请求。 固定时间触发 事件触发固定时间触发,例如在夜间或者服务空闲时间来处理繁忙时候的交易数据,或者运行批量数据,来生成数据报表,通过Serverless方式,不用再额外购买利用率并不高的处理资源。 流量突发场景 弹性扩展应对突发流量 移动互联网应用经常会面对突发流量场景。例如:移动应用的通常流量情况是 QPS 20,但每隔 5 分钟会有一个持续 10s 的 QPS 200 流量(10 倍于通常流量)。传统架构下,企业必须扩展 QPS 200 的硬件能力来应对业务高峰,即使高峰时间仅占整个运行时间的4%。 在Serverless架构下,您可以利用弹性扩展特性,快速构建新的计算能力来满足当前需求,当业务高峰后,资源能够自动释放,有效节省成本。 转码和流量扩容 视频直播某次专场活动,由于无法预估会有多少点播的观众视频接入,把转码和流量扩容这部分内容通过Function来处理,无需考虑并发和流量扩容。 处理大数据场景 由于安全审计问题,您需要从OSS(多个地域)过去一年的数据(1 个小时一个文件)中找出特定关键字访问的日志,同时做聚合运算(计算出总值)。如果使用阿里云函数计算,您将高峰期每 2 小时的访问日志,或者低谷期每 4 小时的访问日志交给一个计算函数处理,并将处理结果存到RDS中。使用一个函数分派数据给另一个函数,使其执行成千上万个相同的实例。 这样会同时运行近千个计算函数(24 x 365 / 10),在不到一分钟的时间内完成整个工作。同样的事情交给ECS+计算脚本来做计算,单单为这些instance配置网络就让人头疼(不同地域无法走内网下载OSS文件):instance的数量可能已经超出了子网中剩余IP地址的数量(比如,您的VPC使用了24位掩码)。 下面结合阿里云的函数计算产品来讲解各个应用场景中地架构以及如何解决场景中的痛点。阿里云的函数计算是基于Serverless这种架构实现的一个全托管产品,用户只需要上传核心代码到函数计算,就可以通过事件源或者SDK&API来运行代码。函数计算会准备好运行环境,并根据请求峰值来动态扩容运行环境。函数计算是按照执行时间来计费,请求处理完成后,计费停止,对于有业务请求有明显高峰和低谷的应用来说,相对节省成本。
剑曼红尘 2020-03-23 15:07:05 0 浏览量 回答数 0

回答

本文介绍如何使用弹性伸缩、消息服务和函数计算,将弹性扩张时创建的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<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); //获取本次伸缩活动对应实例的私网IP List<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 instance for (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; } } 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<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; } }
1934890530796658 2020-03-22 13:32:03 0 浏览量 回答数 0

回答

每一个进程都有自己的内存虚拟地址空间,内存最小的单位是页(page).虚拟地址通过页表(Page Table)映射到物理内存,页表由操作系统维护并被CPU引用.Linux默认的栈(stack)大小为8MB.用户进程部分分段存储内容如下所示(从内存高地址到低地址):栈(stack): 函数参数、返回地址、局部变量等堆(heap): malloc(C)/new(C++)动态分配的内存BSS段(bss): 未初始化或初值为0的全局变量和静态局部变量数据段(data): 已初始化且初值非0的全局变量和静态局部变量代码段(text): 可执行代码、字符串字面值、只读变量详细解释:1.内核空间内核总是驻留在内存中,是操作系统的一部分。内核空间为内核保留,不允许应用程序读写该区域的内容或直接调用内核代码定义的函数。2.栈(stack)栈又称堆栈,由编译器自动分配释放,行为类似数据结构中的栈(先进后出)。堆栈主要有三个用途:(1)为函数内部声明的非静态局部变量(C语言中称“自动变量”)提供存储空间。(2)记录函数调用过程相关的维护性信息,称为栈帧(Stack Frame)或过程活动记录(Procedure Activation Record)。它包括函数返回地址,不适合装入寄存器的函数参数及一些寄存器值的保存。除递归调用外,堆栈并非必需。因为编译时可获知局部变量,参数和返回地址所需空间,并将其分配于BSS段。(3)临时存储区,用于暂存长算术表达式部分计算结果或alloca()函数分配的栈内内存。持续地重用栈空间有助于使活跃的栈内存保持在CPU缓存中,从而加速访问。进程中的每个线程都有属于自己的栈。向栈中不断压入数据时,若超出其容量就会耗尽栈对应的内存区域,从而触发一个页错误。此时若栈的大小低于堆栈最大值RLIMIT_STACK(Linux通常是8M),则栈会动态增长,程序继续运行。映射的栈区扩展到所需大小后,不再收缩。Linux中ulimit -s命令可查看和设置堆栈最大值,当程序使用的堆栈超过该值时, 发生栈溢出(Stack Overflow),程序收到一个段错误(Segmentation Fault)。注意,调高堆栈容量可能会增加内存开销和启动时间。堆栈既可向下增长(向内存低地址)也可向上增长, 这依赖于具体的实现。本文所述堆栈向下增长。堆栈的大小在运行时由内核动态调整。3.内存映射段(mmap)此处,内核将硬盘文件的内容直接映射到内存,任何应用程序都可通过Linux的mmap()系统调用或Windows的CreateFileMapping()/MapViewOfFile()请求这种映射。内存映射是一种方便高效的文件I/O方式,因而被用于装载动态共享库。用户也可创建匿名内存映射,该映射没有对应的文件,可用于存放程序数据。在Linux中,若通过malloc()请求一大块内存,C运行库将创建一个匿名内存映射,而不使用堆内存。“大块”意味着比阈值MMAP_THRESHOLD还大,缺省为128KB,可通过mallopt()调整。该区域用于映射可执行文件用到的动态链接库。在Linux 2.4内核中,若可执行文件依赖共享库,则系统会为这些动态库在从0x40000000开始的地址分配相应空间,并在程序装载时将其载入到该空间。在Linux 2.6内核中,共享库的起始地址被往上移动至更靠近栈区的位置。从进程地址空间的布局可以看到,在有共享库的情况下,留给堆的可用空间还有两处:一处是从BSS段到0x40000000,约不到1GB的空间;另一处是从共享库到栈之间的空间,约不到2GB。这两块空间大小取决于栈、共享库的大小和数量。这样来看,是否应用程序可申请的最大堆空间只有2GB?事实上,这与Linux内核版本有关。在上面给出的进程地址空间经典布局图中,共享库的装载地址为0x40000000,这实际上是Linux kernel 2.6版本之前的情况了,在2.6版本里,共享库的装载地址已经被挪到靠近栈的位置,即位于0xBFxxxxxx附近,因此,此时的堆范围就不会被共享库分割成2个“碎片”,故kernel 2.6的32位Linux系统中,malloc申请的最大内存理论值在2.9GB左右。4.堆(heap)堆用于存放进程运行时动态分配的内存段,可动态扩张或缩减。堆中内容是匿名的,不能按名字直接访问,只能通过指针间接访问。当进程调用malloc(C)/new(C++)等函数分配内存时,新分配的内存动态添加到堆上(扩张);当调用free(C)/delete(C++)等函数释放内存时,被释放的内存从堆中剔除(缩减) 。分配的堆内存是经过字节对齐的空间,以适合原子操作。堆管理器通过链表管理每个申请的内存,由于堆申请和释放是无序的,最终会产生内存碎片。堆内存一般由应用程序分配释放,回收的内存可供重新使用。若程序员不释放,程序结束时操作系统可能会自动回收。堆的末端由break指针标识,当堆管理器需要更多内存时,可通过系统调用brk()和sbrk()来移动break指针以扩张堆,一般由系统自动调用。使用堆时经常出现两种问题:(1) 释放或改写仍在使用的内存(“内存破坏”);(2) 未释放不再使用的内存(“内存泄漏”)。当释放次数少于申请次数时,可能已造成内存泄漏。泄漏的内存往往比忘记释放的数据结构更大,因为所分配的内存通常会圆整为下个大于申请数量的2的幂次(如申请212B,会圆整为256B)。注意,堆不同于数据结构中的”堆”,其行为类似链表。5.BSS段BSS(Block Started by Symbol)段中通常存放程序中以下符号:未初始化的全局变量和静态局部变量.初始值为0的全局变量和静态局部变量(依赖于编译器实现).未定义且初值不为0的符号(该初值即common block的大小).C语言中,未显式初始化的静态分配变量被初始化为0(算术类型)或空指针(指针类型)。由于程序加载时,BSS会被操作系统清零,所以未赋初值或初值为0的全局变量都在BSS中。BSS段仅为未初始化的静态分配变量预留位置,在目标文件中并不占据空间,这样可减少目标文件体积。但程序运行时需为变量分配内存空间,故目标文件必须记录所有未初始化的静态分配变量大小总和(通过start_bss和end_bss地址写入机器代码)。当加载器(loader)加载程序时,将为BSS段分配的内存初始化为0。在嵌入式软件中,进入main()函数之前BSS段被C运行时系统映射到初始化为全零的内存(效率较高)。注意,尽管均放置于BSS段,但初值为0的全局变量是强符号,而未初始化的全局变量是弱符号。若其他地方已定义同名的强符号(初值可能非0),则弱符号与之链接时不会引起重定义错误,但运行时的初值可能并非期望值(会被强符号覆盖)。因此,定义全局变量时,若只有本文件使用,则尽量使用static关键字修饰;否则需要为全局变量定义赋初值(哪怕0值),保证该变量为强符号,以便链接时发现变量名冲突,而不是被未知值覆盖。某些编译器将未初始化的全局变量保存在common段,链接时再将其放入BSS段。在编译阶段可通过-fno-common选项来禁止将未初始化的全局变量放入common段。此外,由于目标文件不含BSS段,故程序烧入存储器(Flash)后BSS段地址空间内容未知。U-Boot(一个嵌入式操作系统引导程序)启动过程中,将U-Boot的Stage2代码(通常位于lib_xxxx/board.c文件)搬迁(拷贝)到SDRAM空间后必须人为添加清零BSS段的代码,而不可依赖于Stage2代码中变量定义时赋0值。BSS段不包含数据,仅维护开始和结束地址,以便内存能在运行时被有效地清零。BSS所需的运行时空间由目标文件记录,但BSS并不占用目标文件内的实际空间,即BSS节段应用程序的二进制映象文件中并不存在。6.数据段(data)数据段通常用于存放程序中已初始化且初值不为0的全局变量和静态局部变量。数据段属于静态内存分配(静态存储区),可读可写。数据段保存在目标文件中(在嵌入式系统里一般固化在镜像文件中),其内容由程序初始化。例如,对于全局变量int gVar = 10,必须在目标文件数据段中保存10这个数据,然后在程序加载时复制到相应的内存。数据段与BSS段的区别如下: (1) BSS段不占用物理文件尺寸,但占用内存空间;数据段占用物理文件,也占用内存空间。对于大型数组如int ar0[10000] = {1, 2, 3, ...}和int ar1[10000],ar1放在BSS段,只记录共有10000*4个字节需要初始化为0,而不是像ar0那样记录每个数据1、2、3...,此时BSS为目标文件所节省的磁盘空间相当可观。(2) 当程序读取数据段的数据时,系统会发出缺页故障,从而分配相应的物理内存;当程序读取BSS段的数据时,内核会将其转到一个全零页面,不会发生缺页故障,也不会为其分配相应的物理内存。运行时数据段和BSS段的整个区段通常称为数据区。某些资料中“数据段”指代数据段 + BSS段 + 堆。7.代码段(text)代码段也称正文段或文本段,通常用于存放程序执行代码(即CPU执行的机器指令)。一般C语言执行语句都编译成机器代码保存在代码段。通常代码段是可共享的,因此频繁执行的程序只需要在内存中拥有一份拷贝即可。代码段通常属于只读,以防止其他程序意外地修改其指令(对该段的写操作将导致段错误)。某些架构也允许代码段为可写,即允许修改程序。代码段指令根据程序设计流程依次执行,对于顺序指令,只会执行一次(每个进程);若有反复,则需使用跳转指令;若进行递归,则需要借助栈来实现。代码段指令中包括操作码和操作对象(或对象地址引用)。若操作对象是立即数(具体数值),将直接包含在代码中;若是局部数据,将在栈区分配空间,然后引用该数据地址;若位于BSS段和数据段,同样引用该数据地址。代码段最容易受优化措施影响。8.保留区(reservd)位于虚拟地址空间的最低部分,未赋予物理地址。任何对它的引用都是非法的,用于捕捉使用空指针和小整型值指针引用内存的异常情况。它并不是一个单一的内存区域,而是对地址空间中受到操作系统保护而禁止用户进程访问的地址区域的总称。大多数操作系统中,极小的地址通常都是不允许访问的,如NULL。C语言将无效指针赋值为0也是出于这种考虑,因为0地址上正常情况下不会存放有效的可访问数据。在32位x86架构的Linux系统中,用户进程可执行程序一般从虚拟地址空间0x08048000开始加载。该加载地址由ELF文件头决定,可通过自定义链接器脚本覆盖链接器默认配置,进而修改加载地址。0x08048000以下的地址空间通常由C动态链接库、动态加载器ld.so和内核VDSO(内核提供的虚拟共享库)等占用。通过使用mmap系统调用,可访问0x08048000以下的地址空间。
a123456678 2019-12-02 02:41:17 0 浏览量 回答数 0

问题

【精品问答】Java实战200例(附源码)

Java实战200例(附源码) 1.编写一个Java程序,用if-else语句判断某年份是否为闰年 2. 编写一个Java程序在屏幕上输出1!+2!+...
珍宝珠 2020-02-14 11:55:46 16104 浏览量 回答数 10

问题

adnroid 3D系列之纹理篇 功能启动 创建纹理 纹理坐标:报错

之前的例子都是纯色的3D模型。今天我们就开始给这些模型贴上华丽丽的纹理,让他们看起来更加接近我们印象中的真实物体。 功能启动 为使用纹理,我们需要打开OpenGL的一些开关以启动我们需要的一些功能:...
kun坤 2020-06-14 10:30:55 0 浏览量 回答数 1

问题

【javascript学习全家桶】934道javascript热门问题,阿里百位技术专家答疑解惑

阿里极客公益活动:或许你挑灯夜战只为一道难题或许你百思不解只求一个答案或许你绞尽脑汁只因一种未知那么他们来了,阿里系技术专家来云栖问答为你解答技术难题了他们用户自己手中的技术来帮助用户成长本次活动特邀百位阿里技术专家对javascript常...
管理贝贝 2019-12-01 20:07:22 6202 浏览量 回答数 1

问题

【阿里云产品公测】以开发者角度看ACE服务『ACE应用构建指南』

评测介绍 评测产品: 云引擎ACE服务开发语言: PHP评测人: mr_wid评测时间: 2014年10月13日-19日 评测概要 非常有幸能够申请到ACE的公测资格, 在本篇评测中, 笔者将以一个开发者的角度来对云引擎...
mr_wid 2019-12-01 21:10:06 20092 浏览量 回答数 6

回答

在开始谈我对架构本质的理解之前,先谈谈对今天技术沙龙主题的个人见解,千万级规模的网站感觉数量级是非常大的,对这个数量级我们战略上 要重 视 它 , 战术上又 要 藐 视 它。先举个例子感受一下千万级到底是什么数量级?现在很流行的优步(Uber),从媒体公布的信息看,它每天接单量平均在百万左右, 假如每天有10个小时的服务时间,平均QPS只有30左右。对于一个后台服务器,单机的平均QPS可以到达800-1000,单独看写的业务量很简单 。为什么我们又不能说轻视它?第一,我们看它的数据存储,每天一百万的话,一年数据量的规模是多少?其次,刚才说的订单量,每一个订单要推送给附近的司机、司机要并发抢单,后面业务场景的访问量往往是前者的上百倍,轻松就超过上亿级别了。 今天我想从架构的本质谈起之后,希望大家理解在做一些建构设计的时候,它的出发点以及它解决的问题是什么。 架构,刚开始的解释是我从知乎上看到的。什么是架构?有人讲, 说架构并不是一 个很 悬 乎的 东西 , 实际 上就是一个架子 , 放一些 业务 和算法,跟我们的生活中的晾衣架很像。更抽象一点,说架构其 实 是 对 我 们 重复性业务 的抽象和我 们 未来 业务 拓展的前瞻,强调过去的经验和你对整个行业的预见。 我们要想做一个架构的话需要哪些能力?我觉得最重要的是架构师一个最重要的能力就是你要有 战 略分解能力。这个怎么来看呢: 第一,你必须要有抽象的能力,抽象的能力最基本就是去重,去重在整个架构中体现在方方面面,从定义一个函数,到定义一个类,到提供的一个服务,以及模板,背后都是要去重提高可复用率。 第二, 分类能力。做软件需要做对象的解耦,要定义对象的属性和方法,做分布式系统的时候要做服务的拆分和模块化,要定义服务的接口和规范。 第三, 算法(性能),它的价值体现在提升系统的性能,所有性能的提升,最终都会落到CPU,内存,IO和网络这4大块上。 这一页PPT举了一些例子来更深入的理解常见技术背后的架构理念。 第一个例子,在分布式系统我们会做 MySQL分 库 分表,我们要从不同的库和表中读取数据,这样的抽象最直观就是使用模板,因为绝大多数SQL语义是相同的,除了路由到哪个库哪个表,如果不使用Proxy中间件,模板就是性价比最高的方法。 第二看一下加速网络的CDN,它是做速度方面的性能提升,刚才我们也提到从CPU、内存、IO、网络四个方面来考虑,CDN本质上一个是做网络智能调度优化,另一个是多级缓存优化。 第三个看一下服务化,刚才已经提到了,各个大网站转型过程中一定会做服务化,其实它就是做抽象和做服务的拆分。第四个看一下消息队列,本质上还是做分类,只不过不是两个边际清晰的类,而是把两个边际不清晰的子系统通过队列解构并且异步化。新浪微博整体架构是什么样的 接下我们看一下微博整体架构,到一定量级的系统整个架构都会变成三层,客户端包括WEB、安卓和IOS,这里就不说了。接着还都会有一个接口层, 有三个主要作用: 第一个作用,要做 安全隔离,因为前端节点都是直接和用户交互,需要防范各种恶意攻击; 第二个还充当着一个 流量控制的作用,大家知道,在2014年春节的时候,微信红包,每分钟8亿多次的请求,其实真正到它后台的请求量,只有十万左右的数量级(这里的数据可能不准),剩余的流量在接口层就被挡住了; 第三,我们看对 PC 端和移 动 端的需求不一样的,所以我们可以进行拆分。接口层之后是后台,可以看到微博后台有三大块: 一个是 平台服 务, 第二, 搜索, 第三, 大数据。到了后台的各种服务其实都是处理的数据。 像平台的业务部门,做的就是 数据存储和读 取,对搜索来说做的是 数据的 检 索,对大数据来说是做的数据的 挖掘。微博其实和淘宝是很类似 微博其实和淘宝是很类似的。一般来说,第一代架构,基本上能支撑到用户到 百万 级别,到第二代架构基本能支撑到 千万 级别都没什么问题,当业务规模到 亿级别时,需要第三代的架构。 从 LAMP 的架构到面向服 务 的架构,有几个地方是非常难的,首先不可能在第一代基础上通过简单的修修补补满足用户量快速增长的,同时线上业务又不能停, 这是我们常说的 在 飞 机上 换 引擎的 问题。前两天我有一个朋友问我,说他在内部推行服务化的时候,把一个模块服务化做完了,其他部门就是不接。我建议在做服务化的时候,首先更多是偏向业务的梳理,同时要找准一个很好的切入点,既有架构和服务化上的提升,业务方也要有收益,比如提升性能或者降低维护成本同时升级过程要平滑,建议开始从原子化服务切入,比如基础的用户服务, 基础的短消息服务,基础的推送服务。 第二,就是可 以做无状 态 服 务,后面会详细讲,还有数据量大了后需要做数据Sharding,后面会将。 第三代 架构 要解决的 问题,就是用户量和业务趋于稳步增加(相对爆发期的指数级增长),更多考虑技术框架的稳定性, 提升系统整体的性能,降低成本,还有对整个系统监控的完善和升级。 大型网站的系统架构是如何演变的 我们通过通过数据看一下它的挑战,PV是在10亿级别,QPS在百万,数据量在千亿级别。我们可用性,就是SLA要求4个9,接口响应最多不能超过150毫秒,线上所有的故障必须得在5分钟内解决完。如果说5分钟没处理呢?那会影响你年终的绩效考核。2015年微博DAU已经过亿。我们系统有上百个微服务,每周会有两次的常规上线和不限次数的紧急上线。我们的挑战都一样,就是数据量,bigger and bigger,用户体验是faster and faster,业务是more and more。互联网业务更多是产品体验驱动, 技 术 在 产 品 体验上最有效的贡献 , 就是你的性能 越来越好 。 每次降低加载一个页面的时间,都可以间接的降低这个页面上用户的流失率。微博的技术挑战和正交分解法解析架构 下面看一下 第三代的 架构 图 以及 我 们 怎么用正交分解法 阐 述。 我们可以看到我们从两个维度,横轴和纵轴可以看到。 一个 维 度 是 水平的 分层 拆分,第二从垂直的维度会做拆分。水平的维度从接口层、到服务层到数据存储层。垂直怎么拆分,会用业务架构、技术架构、监控平台、服务治理等等来处理。我相信到第二代的时候很多架构已经有了业务架构和技术架构的拆分。我们看一下, 接口层有feed、用户关系、通讯接口;服务层,SOA里有基层服务、原子服务和组合服务,在微博我们只有原子服务和组合服务。原子服务不依赖于任何其他服务,组合服务由几个原子服务和自己的业务逻辑构建而成 ,资源层负责海量数据的存储(后面例子会详细讲)。技 术框架解决 独立于 业务 的海量高并发场景下的技术难题,由众多的技术组件共同构建而成 。在接口层,微博使用JERSY框架,帮助你做参数的解析,参数的验证,序列化和反序列化;资源层,主要是缓存、DB相关的各类组件,比如Cache组件和对象库组件。监 控平台和服 务 治理 , 完成系统服务的像素级监控,对分布式系统做提前诊断、预警以及治理。包含了SLA规则的制定、服务监控、服务调用链监控、流量监控、错误异常监控、线上灰度发布上线系统、线上扩容缩容调度系统等。 下面我们讲一下常见的设计原则。 第一个,首先是系统架构三个利器: 一个, 我 们 RPC 服 务组 件 (这里不讲了), 第二个,我们 消息中 间 件 。消息中间件起的作用:可以把两个模块之间的交互异步化,其次可以把不均匀请求流量输出为匀速的输出流量,所以说消息中间件 异步化 解耦 和流量削峰的利器。 第三个是配置管理,它是 代码级灰度发布以及 保障系统降级的利器。 第二个 , 无状态 , 接口 层 最重要的就是无状 态。我们在电商网站购物,在这个过程中很多情况下是有状态的,比如我浏览了哪些商品,为什么大家又常说接口层是无状态的,其实我们把状态从接口层剥离到了数据层。像用户在电商网站购物,选了几件商品,到了哪一步,接口无状态后,状态要么放在缓存中,要么放在数据库中, 其 实 它并不是没有状 态 , 只是在 这 个 过 程中我 们 要把一些有状 态 的 东 西抽离出来 到了数据层。 第三个, 数据 层 比服 务层 更需要 设计,这是一条非常重要的经验。对于服务层来说,可以拿PHP写,明天你可以拿JAVA来写,但是如果你的数据结构开始设计不合理,将来数据结构的改变会花费你数倍的代价,老的数据格式向新的数据格式迁移会让你痛不欲生,既有工作量上的,又有数据迁移跨越的时间周期,有一些甚至需要半年以上。 第四,物理结构与逻辑结构的映射,上一张图看到两个维度切成十二个区间,每个区间代表一个技术领域,这个可以看做我们的逻辑结构。另外,不论后台还是应用层的开发团队,一般都会分几个垂直的业务组加上一个基础技术架构组,这就是从物理组织架构到逻辑的技术架构的完美的映射,精细化团队分工,有利于提高沟通协作的效率 。 第五, www .sanhao.com 的访问过程,我们这个架构图里没有涉及到的,举个例子,比如当你在浏览器输入www.sanhao网址的时候,这个请求在接口层之前发生了什么?首先会查看你本机DNS以及DNS服务,查找域名对应的IP地址,然后发送HTTP请求过去。这个请求首先会到前端的VIP地址(公网服务IP地址),VIP之后还要经过负载均衡器(Nginx服务器),之后才到你的应用接口层。在接口层之前发生了这么多事,可能有用户报一个问题的时候,你通过在接口层查日志根本发现不了问题,原因就是问题可能发生在到达接口层之前了。 第六,我们说分布式系统,它最终的瓶颈会落在哪里呢?前端时间有一个网友跟我讨论的时候,说他们的系统遇到了一个瓶颈, 查遍了CPU,内存,网络,存储,都没有问题。我说你再查一遍,因为最终你不论用上千台服务器还是上万台服务器,最终系统出瓶颈的一定会落在某一台机(可能是叶子节点也可能是核心的节点),一定落在CPU、内存、存储和网络上,最后查出来问题出在一台服务器的网卡带宽上。微博多级双机房缓存架构 接下来我们看一下微博的Feed多级缓存。我们做业务的时候,经常很少做业务分析,技术大会上的分享又都偏向技术架构。其实大家更多的日常工作是需要花费更多时间在业务优化上。这张图是统计微博的信息流前几页的访问比例,像前三页占了97%,在做缓存设计的时候,我们最多只存最近的M条数据。 这里强调的就是做系统设计 要基于用 户 的 场 景 , 越细致越好 。举了一个例子,大家都会用电商,电商在双十一会做全国范围内的活动,他们做设计的时候也会考虑场景的,一个就是购物车,我曾经跟相关开发讨论过,购物车是在双十一之前用户的访问量非常大,就是不停地往里加商品。在真正到双十一那天他不会往购物车加东西了,但是他会频繁的浏览购物车。针对这个场景,活动之前重点设计优化购物车的写场景, 活动开始后优化购物车的读场景。 你看到的微博是由哪些部分聚合而成的呢?最右边的是Feed,就是微博所有关注的人,他们的微博所组成的。微博我们会按照时间顺序把所有关注人的顺序做一个排序。随着业务的发展,除了跟时间序相关的微博还有非时间序的微博,就是会有广告的要求,增加一些广告,还有粉丝头条,就是拿钱买的,热门微博,都会插在其中。分发控制,就是说和一些推荐相关的,我推荐一些相关的好友的微博,我推荐一些你可能没有读过的微博,我推荐一些其他类型的微博。 当然对非时序的微博和分发控制微博,实际会起多个并行的程序来读取,最后同步做统一的聚合。这里稍微分享一下, 从SNS社交领域来看,国内现在做的比较好的三个信息流: 微博 是 基于弱关系的媒体信息流 ; 朋友圈是基于 强 关系的信息流 ; 另外一个做的比 较 好的就是今日 头 条 , 它并不是基于关系来构建信息流 , 而是基于 兴趣和相关性的个性化推荐 信息流 。 信息流的聚合,体现在很多很多的产品之中,除了SNS,电商里也有信息流的聚合的影子。比如搜索一个商品后出来的列表页,它的信息流基本由几部分组成:第一,打广告的;第二个,做一些推荐,热门的商品,其次,才是关键字相关的搜索结果。 信息流 开始的时候 很 简单 , 但是到后期会 发现 , 你的 这 个流 如何做控制分发 , 非常复杂, 微博在最近一两年一直在做 这样 的工作。刚才我们是从业务上分析,那么技术上怎么解决高并发,高性能的问题?微博访问量很大的时候,底层存储是用MySQL数据库,当然也会有其他的。对于查询请求量大的时候,大家知道一定有缓存,可以复用可重用的计算结果。可以看到,发一条微博,我有很多粉丝,他们都会来看我发的内容,所以 微博是最适合使用 缓 存 的系统,微博的读写比例基本在几十比一。微博使用了 双 层缓 存,上面是L1,每个L1上都是一组(包含4-6台机器),左边的框相当于一个机房,右边又是一个机房。在这个系统中L1缓存所起的作用是什么? 首先,L1 缓 存增加整个系 统 的 QPS, 其次 以低成本灵活扩容的方式 增加 系统 的 带宽 。想象一个极端场景,只有一篇博文,但是它的访问量无限增长,其实我们不需要影响L2缓存,因为它的内容存储的量小,但它就是访问量大。这种场景下,你就需要使用L1来扩容提升QPS和带宽瓶颈。另外一个场景,就是L2级缓存发生作用,比如我有一千万个用户,去访问的是一百万个用户的微博 ,这个时候,他不只是说你的吞吐量和访问带宽,就是你要缓存的博文的内容也很多了,这个时候你要考虑缓存的容量, 第二 级缓 存更多的是从容量上来 规划,保证请求以较小的比例 穿透到 后端的 数据 库 中 ,根据你的用户模型你可以估出来,到底有百分之多少的请求不能穿透到DB, 评估这个容量之后,才能更好的评估DB需要多少库,需要承担多大的访问的压力。另外,我们看双机房的话,左边一个,右边一个。 两个机房是互 为 主 备 , 或者互 为热备 。如果两个用户在不同地域,他们访问两个不同机房的时候,假设用户从IDC1过来,因为就近原理,他会访问L1,没有的话才会跑到Master,当在IDC1没找到的时候才会跑到IDC2来找。同时有用户从IDC2访问,也会有请求从L1和Master返回或者到IDC1去查找。 IDC1 和 IDC2 ,两个机房都有全量的用户数据,同时在线提供服务,但是缓存查询又遵循最近访问原理。还有哪些多级缓存的例子呢?CDN是典型的多级缓存。CDN在国内各个地区做了很多节点,比如在杭州市部署一个节点时,在机房里肯定不止一台机器,那么对于一个地区来说,只有几台服务器到源站回源,其他节点都到这几台服务器回源即可,这么看CDN至少也有两级。Local Cache+ 分布式 缓 存,这也是常见的一种策略。有一种场景,分布式缓存并不适用, 比如 单 点 资 源 的爆发性峰值流量,这个时候使用Local Cache + 分布式缓存,Local Cache 在 应用 服 务 器 上用很小的 内存资源 挡住少量的 极端峰值流量,长尾的流量仍然访问分布式缓存,这样的Hybrid缓存架构通过复用众多的应用服务器节点,降低了系统的整体成本。 我们来看一下 Feed 的存 储 架构,微博的博文主要存在MySQL中。首先来看内容表,这个比较简单,每条内容一个索引,每天建一张表,其次看索引表,一共建了两级索引。首先想象一下用户场景,大部分用户刷微博的时候,看的是他关注所有人的微博,然后按时间来排序。仔细分析发现在这个场景下, 跟一个用户的自己的相关性很小了。所以在一级索引的时候会先根据关注的用户,取他们的前条微博ID,然后聚合排序。我们在做哈希(分库分表)的时候,同时考虑了按照UID哈希和按照时间维度。很业务和时间相关性很高的,今天的热点新闻,明天就没热度了,数据的冷热非常明显,这种场景就需要按照时间维度做分表,首先冷热数据做了分离(可以对冷热数据采用不同的存储方案来降低成本),其次, 很容止控制我数据库表的爆炸。像微博如果只按照用户维度区分,那么这个用户所有数据都在一张表里,这张表就是无限增长的,时间长了查询会越来越慢。二级索引,是我们里面一个比较特殊的场景,就是我要快速找到这个人所要发布的某一时段的微博时,通过二级索引快速定位。 分布式服务追踪系统 分布式追踪服务系统,当系统到千万级以后的时候,越来越庞杂,所解决的问题更偏向稳定性,性能和监控。刚才说用户只要有一个请求过来,你可以依赖你的服务RPC1、RPC2,你会发现RPC2又依赖RPC3、RPC4。分布式服务的时候一个痛点,就是说一个请求从用户过来之后,在后台不同的机器之间不停的调用并返回。 当你发现一个问题的时候,这些日志落在不同的机器上,你也不知道问题到底出在哪儿,各个服务之间互相隔离,互相之间没有建立关联。所以导致排查问题基本没有任何手段,就是出了问题没法儿解决。 我们要解决的问题,我们刚才说日志互相隔离,我们就要把它建立联系。建立联系我们就有一个请求ID,然后结合RPC框架, 服务治理功能。假设请求从客户端过来,其中包含一个ID 101,到服务A时仍然带有ID 101,然后调用RPC1的时候也会标识这是101 ,所以需要 一个唯一的 请求 ID 标识 递归迭代的传递到每一个 相关 节点。第二个,你做的时候,你不能说每个地方都加,对业务系统来说需要一个框架来完成这个工作, 这 个框架要 对业务 系 统 是最低侵入原 则 , 用 JAVA 的 话 就可以用 AOP,要做到零侵入的原则,就是对所有相关的中间件打点,从接口层组件(HTTP Client、HTTP Server)至到服务层组件(RPC Client、RPC Server),还有数据访问中间件的,这样业务系统只需要少量的配置信息就可以实现全链路监控 。为什么要用日志?服务化以后,每个服务可以用不同的开发语言, 考虑多种开发语言的兼容性 , 内部定 义标 准化的日志 是唯一且有效的办法。最后,如何构建基于GPS导航的路况监控?我们刚才讲分布式服务追踪。分布式服务追踪能解决的问题, 如果 单一用 户发现问题 后 , 可以通 过请 求 ID 快速找到 发 生 问题 的 节 点在什么,但是并没有解决如何发现问题。我们看现实中比较容易理解的道路监控,每辆车有GPS定位,我想看北京哪儿拥堵的时候,怎么做? 第一个 , 你肯定要知道每个 车 在什么位置,它走到哪儿了。其实可以说每个车上只要有一个标识,加上每一次流动的信息,就可以看到每个车流的位置和方向。 其次如何做 监 控和 报 警,我们怎么能了解道路的流量状况和负载,并及时报警。我们要定义这条街道多宽多高,单位时间可以通行多少辆车,这就是道路的容量。有了道路容量,再有道路的实时流量,我们就可以基于实习路况做预警? 对应于 分布式系 统 的话如何构建? 第一 , 你要 定义 每个服 务节 点它的 SLA A 是多少 ?SLA可以从系统的CPU占用率、内存占用率、磁盘占用率、QPS请求数等来定义,相当于定义系统的容量。 第二个 , 统计 线 上 动态 的流量,你要知道服务的平均QPS、最低QPS和最大QPS,有了流量和容量,就可以对系统做全面的监控和报警。 刚才讲的是理论,实际情况肯定比这个复杂。微博在春节的时候做许多活动,必须保障系统稳定,理论上你只要定义容量和流量就可以。但实际远远不行,为什么?有技术的因素,有人为的因素,因为不同的开发定义的流量和容量指标有主观性,很难全局量化标准,所以真正流量来了以后,你预先评估的系统瓶颈往往不正确。实际中我们在春节前主要采取了三个措施:第一,最简单的就是有降 级 的 预 案,流量超过系统容量后,先把哪些功能砍掉,需要有明确的优先级 。第二个, 线上全链路压测,就是把现在的流量放大到我们平常流量的五倍甚至十倍(比如下线一半的服务器,缩容而不是扩容),看看系统瓶颈最先发生在哪里。我们之前有一些例子,推测系统数据库会先出现瓶颈,但是实测发现是前端的程序先遇到瓶颈。第三,搭建在线 Docker 集群 , 所有业务共享备用的 Docker集群资源,这样可以极大的避免每个业务都预留资源,但是实际上流量没有增长造成的浪费。 总结 接下来说的是如何不停的学习和提升,这里以Java语言为例,首先, 一定要 理解 JAVA;第二步,JAVA完了以后,一定要 理 解 JVM;其次,还要 理解 操作系统;再次还是要了解一下 Design Pattern,这将告诉你怎么把过去的经验抽象沉淀供将来借鉴;还要学习 TCP/IP、 分布式系 统、数据结构和算法。
hiekay 2019-12-02 01:39:25 0 浏览量 回答数 0

问题

应该返回false的用户输入返回true

到目前为止,我一直在程序中使用运算符比较所有字符串。但是,我遇到了一个错误,将其中一个更改为错误.equals(),并修复了该错误。 true==falseÿ...
养狐狸的猫 2019-12-01 20:00:45 8 浏览量 回答数 0

问题

【Java学习全家桶】1460道Java热门问题,阿里百位技术专家答疑解惑

阿里极客公益活动: 或许你挑灯夜战只为一道难题 或许你百思不解只求一个答案 或许你绞尽脑汁只因一种未知 那么他们来了,阿里系技术专家来云栖问答为你解答技术难题了 他们用户自己手中的技术来帮助用户成长 本次活动特邀百位阿里技术专家对Java常...
管理贝贝 2019-12-01 20:07:15 27612 浏览量 回答数 19

问题

厉华:写一个开源容器引擎会是什么样的体验? 热:报错

2013年,Docker.Inc 开源了一款应用容器引擎 Docker。开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到相同内核的任何 Linux 机器上部署运行。这种集装箱式的应用开发和部署方...
kun坤 2020-06-10 10:01:12 3 浏览量 回答数 1

云产品推荐

上海奇点人才服务相关的云产品 小程序定制 上海微企信息技术相关的云产品 国内短信套餐包 ECS云服务器安全配置相关的云产品 开发者问答 阿里云建站 自然场景识别相关的云产品 万网 小程序开发制作 视频内容分析 视频集锦 代理记账服务