阿里云迁移AWS视频点播技术攻坚

文章目录

  • 🐷 背景
  • 🦥 简述
  • 🐥 Aws服务
  • 🦜 AWS CloudFormation
  • 🐞 问题
  • 🐉 落地方案
  • 🦉 Aws vs Aliyun
  • 🍄 避坑指南

🐷 背景

由于AWS整体成本略低于阿里云,公司决定将文件存储模块的业务迁移至AWS,迁移过程中发现,关于视频播放,转码之前阿里云已自动集成相关功能,但Aws并没有原生支持,为此踩了很多坑!

🦥 简述

本次主要是完成视频上传Aws并完成转码播放功能,基础功能已实现,但速度方面略低于阿里云,后续还需要优化改进。

Aws指南

🐥 Aws服务

  • Amazon Elemental MediaConvert 将媒体文件从源格式转码为可在智能手机、平板电 脑、PC 和其他设备播放的流媒体格式。
  • Amazon CloudFront 加速将您的视频内容分发给最终用户。
  • Amazon Step Functions 讲执行不同任务的离散函数整合为工作流。
  • Amazon Lambda 让您无需预置或管理服务器即可运行代码。
  • Amazon Simple Storage Service (Amazon S3) 用于对象存储。
  • Amazon DynamoDB 用于追踪源和目标文件的元数据以及工作流程的进度。
  • Amazon CloudWatch 用于追踪转码任务。
  • Amazon Simple Queue Service (Amazon SQS) 用于捕获工作流的输出。
  • Amazon Simple Notification Service (Amazon SNS) 用于发送发布、编码和错误的通知信息。

按照aws解决方案库提供的方案,部署大概需要20分钟,部署完成后怎么使用,部署过程都做了哪些事情,整体流程是怎么样的,从哪些环节可以接入自定义的逻辑和程序。
除了以上部署方案提到的服务,本身该部署方案也是一个服务AWS CloudFormation。
根据本次部署和开发经历,建议需要使用该解决方案的小伙伴不要着急部署,先通过各种学习途径了解以上服务,尤其是标红的服务是做什么的,什么应用场景下使用。

B站学习资料:AWS认证解决方案全栈架构师 中文课程

🦜 AWS CloudFormation


CloudFormation是一个模板,该模板包含一套解决方案所需的所有资源,将一个通用解决方案配置成一个模板,需要再次部署相同解决方案则使用模板即可快速实现复制。启动堆栈可对模板进行部署实施。

堆栈部署完成,删除堆栈可将堆栈相关的所有资源进行一并删除,想部署或尝试的可以放心实施,不用删除即可!

Aws点播使用的模板


整体转码流程

官方服务说明


堆栈部署产生的aws服务说明


mediaconvert作业模板


Lambda

Lambda很重要!Lambda很重要!Lambda很重要!


IAM Role

IAM很重要!IAM很重要!IAM很重要!

SQS

SQS消息队列,转码完成后会向消息队列写入消息,业务侧可读取消息实现转码完成事件通知。

🐞 问题

使用Workflow trigger选项VideoFile的话,部署的方案会默认转码成m3u8文件;会默认使用3个模板进行转码(MediaConvert_Template_2160p、MediaConvert_Template_1080p、MediaConvert_Template_720p)于现有阿里云转码成单个mp4文件不一致,现有业务对多种品质输出没有要求,需要能够定制转码输出格式。

备注:如要实现类似点播清晰度选择的话可考虑多个质量转码模板。
解决方案在github上

🐉 落地方案

调整堆栈参数

Workflow trigger选择MetadataFile ,工作流触发参数由VideoFile调整为Metadata,堆栈会进行自动调整。
调整后s3桶事件通知中由之前的视频文件触发工作流,变更为由metadata触发,metadata就是.json的一个文件,具体内容参考github

Metadata文件创建

堆栈调整后,上传视频文件不再触发工作流,此时需要对上传的视频文件生成对应的metadata文件(.json文件),可以由业务侧进行两次上传,先上传视频文件,再上传metadata文件,进而触发工作流。考虑存在失败的可能以及业务侧需要实现额外的逻辑,因此使用了Lambda创建Metadata文件,文件上传完成触发Lambda,由Lambda实现生成Metadata文件。

const aws = require('aws-sdk');const s3 = new aws.S3({ apiVersion: '2006-03-01' });exports.handler = async (event, context) => {// Get the object from the event and show its content typeconst bucket = event.Records[0].s3.bucket.name;const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));if(key.endsWith('.json')){console.log('json file');return;}const params = {Bucket: bucket,Key: key,}; console.log(params);const metadata={"srcVideo": key,"ArchiveSource": true,"FrameCapture":true,"JobTemplate":"jiaoyubaoCompanyVideo_trans_template"};const metadataKey=key.substring(0,key.lastIndexOf('.'))+'.json';console.log('metadatakey:',metadataKey);try {//const { ContentType } = await s3.headObject(params).promise();//console.log('CONTENT TYPE:', ContentType);//upload metadataconst uploadParams = {Bucket: bucket, Key: metadataKey, Body: JSON.stringify(metadata)};console.log(uploadParams);// call S3 to retrieve upload file to specified bucketconst res=await s3.upload (uploadParams).promise();return res;} catch (err) {console.log(err);throw err;}
};

自定义转码模板

默认模板转码为m3u8格式文件,需要实现mp4格式转码,自定义转码模板jiaoyubaoCompanyVideo_trans_template

质量优化级别建议使用单一传递 HQ,太低转码文件质量较低,但文件大小会降低明显,考虑效果,采用中间质量。
最大比特率(位/秒)设置为了2000000,该值会影响转码视频大小,实际测试2000000转码视频较源视频大小略有压缩,固采用该值,如需高质量可参考该值设置更大的值,如需更多的压缩,可降低该值。

完成以上三部,即可通过aws控制台上传视频,触发转码工作流,输出转码视频了

业务侧上传视频

机构后台限制了视频大小50M,考虑到后台上传会导致占用更多的流量,和服务器资源,因此使用客户端直传方案。
使用jssdk实现web直传。

客户端直连S3实现分片续传思路与实践

https://aws.amazon.com/cn/blogs/china/s3-multipul-upload-practice/

授权方案

方案一:STS临时授权

由于客户端直传,使用长久AK(AwsAccessKeyId、AwsSecretAccessKey)的话会造成敏感信息泄漏,存在安全隐患。
临时授权需要一个Role(角色)用于创建一个临时的凭证,前端使用临时凭证上传s3。
为了安全考虑,该角色拥有可用的最小权限,角色绑定策略实现权限限制。因此需要先创建一个策略,然后绑定到该角色。

策略:ClientUploadVideoS3Policy

{"Version": "2012-10-17","Statement": [{"Sid": "VisualEditor0","Effect": "Allow","Action": ["s3:PutObject","s3:GetObject"],"Resource": "arn:aws-cn:s3:::videoondemand-company-source234242b7/*"}]
}

角色:ClientVideoUploadAssumeRole


AssumeRole

private Amazon.SecurityToken.Model.AssumeRoleResponse GetOriginalAwsAssumeRole(int durationSeconds=3600)
{//Amazon Resource Namevar roleArnToAssume = "arn:aws-cn:iam::77480923422862:role/ClientVideoUploadAssumeRole";var client = new Amazon.SecurityToken.AmazonSecurityTokenServiceClient(AWSCredentialsConfig.AwsAccessKeyId,AWSCredentialsConfig.AwsSecretAccessKey,Amazon.RegionEndpoint.CNNorthWest1);// Create the request to use with the AssumeRoleAsync call.var assumeRoleReq = new Amazon.SecurityToken.Model.AssumeRoleRequest(){DurationSeconds = durationSeconds,RoleSessionName = "AwsUpload",RoleArn = roleArnToAssume};var assumeRoleRes =  client.AssumeRole(assumeRoleReq);return assumeRoleRes;
}
{"AssumedRoleUser": {"Arn": "arn:aws-cn:sts::77421422862:assumed-role/ClientVideoUploadAssumeRole/AwsUpload","AssumedRoleId": "AROA3IZSCNY432ZRBYTQU5:AwsUpload"},"Credentials": {"AccessKeyId": "ASIA3234fsdfwe42Q3UK6","Expiration": "2022-12-30T15:24:37+08:00","SecretAccessKey": "khoROTW8vyDSD7tcaDmvMZg7S0Ujte6TiRP+ql2b","SessionToken": "IQoJb3JpZ2luX2VjEGEaDmNuLW5vcnRod2VzdC0xIkYwRAIgUZ7b3+OGVn+agrp3IJ+JRQDjTTgKcGW+F92eOi0CsMICICqooZD6FJoCnOdBNaAPfrvzB5iG4fxzSQL234234uEKp8CCI///wEQABoMNzc0ODA5NzQyODYyIgw+fkUE58kq72ukAWkq8wEbHoeO5inZTKxAr/kafYBQ5RFOyYx8EJjrYd/uCReS6Te/Ix64D9/aqSM/BimyACmnjM3BmeDuys12zYDdHLfOAhuOUR4dyjKEXPWTwV6usxVb+5EYw4T9Z47dX9gV8EoI0n342342jzKnY8iQIikpidsYFjo6g27Q/dddCUXGqhIUB0QtzAhoMRAM9ndVl9w2VH7CBUsFPFXPhUaxhhIol59p8L2342342BKkFFpY/Zf8wSMLBYLLO3p9/uCBA4D06chibtYVoiIPLoMvPDlOEU6iAk+LtKTOtdmZ5/J8+WTGeZB8Y6R7II14wpe++rAY6ngGPSJw/q0JoPm8WMMH84c3Lv47V9n9sDTSX8WHlASPvgySRiuP+DvSd3RqMDootuk3awg1x3hvzU438xJ+BCo5qAWFIJE2niwyslWh3zrxiGADBAqqg/XT2P4yQg8wiscQh38KxSOlj+LxgvTcdVfe4/Yy4uWtudvWR4EkcVvpWWIw+sxDkX0LlYvfHz3yhAq209Gt8E5zcX4NWQ8mnug=="},"PackedPolicySize": 0,"SourceIdentity": null,"ResponseMetadata": {"RequestId": "15c423624-923f4-452b-902e-1e8561175523","Metadata": {},"ChecksumAlgorithm": 0,"ChecksumValidationStatus": 0},"ContentLength": 1463,"HttpStatusCode": 200
}

方案二:使用预签名 URL(未验证)

https://docs.amazonaws.cn/AmazonS3/latest/userguide/using-presigned-url.html

jssdk直传

<script src="/js/aws-sdk-2.45.0.min.js"></script><script>var accessKeyId = "{$:token.Credentials.AccessKeyId}";var accessKeySecret = "{$:token.Credentials.SecretAccessKey}";var sessionToken = "{$:token.Credentials.SessionToken}";var endpoint = `${location.protocol}//s3.cn-northwest-1.amazonaws.com.cn`;var s3 = new AWS.S3({ accessKeyId: accessKeyId, secretAccessKey: accessKeySecret, sessionToken: sessionToken, region: 'cn-northwest-1', endpoint: endpoint, signatureVersion: 'v4' });var filePicker = document.getElementById("videoPicker");var progress = document.getElementById("progress");filePicker.addEventListener('change', function (event) {console.log(event.target.files[0]);if (event.target.files[0].size / 1024 / 1024 > 50) {alert("文件超出大小限制,不得超过50M");document.getElementById("videoPicker").value = null;return;}});function start() {if (filePicker.files.length <= 0) {alert("请先选择视频");return;}var file = filePicker.files[0];var ext = file.name.substring(file.name.lastIndexOf('.'));$("#progressInfo").css({ width: '100%' });filePicker.setAttribute("disabled", "disabled");var params = {Bucket: 'videoondemand-company-source-gbwnqmc885b7',Key: `{$:companyInfo.comid}_${Date.now()}${ext}`,Body:file};var upload = s3.upload(params, {queueSize: 1,//分片上传并发队列,代表了能同时上传的分片数量connectTimeout: 60 * 1000 * 10,httpOptions: {timeout: 60 * 1000 * 10}}).on('httpUploadProgress', function (e) {var percent = parseInt(e.loaded, 10) / parseInt(e.total, 10);percent = percent.toFixed(2) * 100;setTimeout(function () {var p = percent + "%";$(".progress span").css({ width: p });});});upload.send(function (err, data) {console.log(err);console.log(data);if (err) {alert("上传失败,请稍后重试");$("#progressInfo").css({ width: '0' });}//上传成功$.ajax({url: '/CompanyHandler.ashx',type: 'post',dataType: 'json',data: { action: 'saveVideo', object: data.Key, rand: Math.random() },success: function (result) {if (result.result == 1) {//关闭弹窗,刷新列表页parent.location.reload();}else {alert(result.description);}},error: function () {alert("发生异常,请稍后重试或联系客服反馈");}});filePicker.removeAttribute("disabled");});}</script>

S3 CORS


[{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["https://*.cn"],"ExposeHeaders": ["ETag"]},{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["http://*.jiaoyubao.cn"],"ExposeHeaders": ["ETag"]},{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["http://localhost:53762"],"ExposeHeaders": ["ETag"]}
]

SQS异步通知机制

转码完成后会将完成消息写入SQS,业务侧可以通过读取SQS拿到消息,进行业务侧数据和逻辑完善。可以实现一个服务轮询SQS那消息,但由于SQS按照接口调用次数收费以及可以预测的大量的空队列读取情况,空队列读取依旧计入接口调用次数(aws有100万次的接口调用免费额度)。
基于以上考虑,使用SQSLambda 触发器实现推送机制,有消息到达SQS则触发Lambda函数,将消息推送给业务侧,需要自行编写Lambda函数。

VideoonDemand-Company-Notify

const http = require('http')exports.handler = async (event) => {const data = JSON.stringify(event);console.log(data)//其他业务复用当前部署堆栈的话,异步通知需要差异化的话,可以考虑通过视频前缀约束在此处进行差异化处理。const options = {hostname: 'vip3.shhddg.cn',port: 80,path: '/awsnotifications.aspx',method: 'POST',headers: {'Content-Type': 'application/json','Content-Length': data.length}}await new Promise(function (resolve, reject) {const req = http.request(options, res => {console.log(`状态码: ${res.statusCode}`)res.on('data', d => {})});req.on('error', error => {console.error(error)});req.write(data);req.end();                         });return event;
};

VideoonDemand-Company-Notify 要能够被SQS触发,需要配置对应的权限,创建的Lambda的执行角色需要加入AWSLambdaSQSExecutionRole 权限策略



域名解析到S3

将域名解析到S3 转码结果目标桶,并配置S3公开访问,从而可以使用域名访问S3资源。域名需要进行解析,注意需要去掉Host转发


CloudFormation部署的堆栈,默认会在当前账户创建CloudFront用于域名解析,仅做Dns解析调整即可。堆栈默认对S3桶权限做了配置。但目前的CloudFront统一在另外账户,不在当前部署堆栈的账户,因此,需要将域名直接解析到S3桶,并对S3桶设置了公开访问和策略调整,Principal设置了所有,默认是堆栈创建的对应域名的CloudFront用户允许。

🦉 Aws vs Aliyun

机构后台使用STS临时授权方案上传S3,对比阿里云接口调用和方式,Aws和阿里云从风格到接口基本一致。

阿里云

private AssumeRoleResponse GetStsToken()
{//获取sts凭证const string REGIONID = "cn-hangzhou";const string ENDPOINT = "sts.cn-yuenan.aliyuncs.com";// 构建一个 Aliyun Client, 用于发起请求// 构建Aliyun Client时需要设置AccessKeyId和AccessKeySevcretDefaultProfile.AddEndpoint(REGIONID, REGIONID, "Sts", ENDPOINT);IClientProfile profile = DefaultProfile.GetProfile(REGIONID, AppCode.ConstSetting.AccessKeyId, AppCode.ConstSetting.AccessKeySecret);DefaultAcsClient client = new DefaultAcsClient(profile);// 构造AssumeRole请求AssumeRoleRequest request = new AssumeRoleRequest();request.AcceptFormat = FormatType.JSON;// 指定角色Arnrequest.RoleArn = "acs:ram::14898694342448426:role/jiaoyubaovideorole";request.RoleSessionName = "upload";// 可以设置Token有效期,可选参数,默认3600秒;// request.DurationSeconds = 3600;// 可以设置Token的附加Policy,可以在获取Token时,通过额外设置一个Policy进一步减小Token的权限;// request.Policy="<policy-content>"try{AssumeRoleResponse response = client.GetAcsResponse(request);//Token过期时间;服务器返回UTC时间,这里转换成北京时间显示;return response;}catch (Exception ex){//Console.Write(ex.ToString());return null;}
}

Aws

private Amazon.SecurityToken.Model.AssumeRoleResponse GetOriginalAwsAssumeRole(int durationSeconds=3600)
{var roleArnToAssume = "arn:aws-cn:iam::7743242742862:role/ClientVideoUploadAssumeRole";var client = new Amazon.SecurityToken.AmazonSecurityTokenServiceClient(AWSCredentialsConfig.AwsAccessKeyId,AWSCredentialsConfig.AwsSecretAccessKey,Amazon.RegionEndpoint.CNNorthWest1);// Create the request to use with the AssumeRoleAsync call.var assumeRoleReq = new Amazon.SecurityToken.Model.AssumeRoleRequest(){DurationSeconds = durationSeconds,RoleSessionName = "AwsUpload",RoleArn = roleArnToAssume};var assumeRoleRes =  client.AssumeRole(assumeRoleReq);return assumeRoleRes;
}

🍄 避坑指南

坑1:文档版本不一致,不好找

服务商给到的视频点播文档中的方案链接https://www.amazonaws.cn/solutions/video-on-demand/?nc1=h_ls

搜索引擎或官方给到的视频点播文档链接https://aws.amazon.com/cn/solutions/implementations/video-on-demand-on-aws/

这两个链接的github源码不一致,而实现自定义定制功能的解决方案,在第一个链接中可以找到,第二个链接找不到。

坑2:SQS Lambda 触发器需要额外的权限

The provided execution role does not have permissions to call ReceiveMessage
Lambda函数 配置-权限-执行角色 的权限策略需要添加AWSLambdaSQSExecutionRole 策略

坑3:S3 CORS

分片上传ETag问题,使用分片上传的话ExposeHeaders务必设置ETag,否则会存在跨域问题,可参考以下文档
https://www.cnblogs.com/duhuo/p/14828021.html

[{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["https://*.hfdhsa.cn"],"ExposeHeaders": ["ETag" //坑]},{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["http://*.geertfd.cn"],"ExposeHeaders": ["ETag"]},{"AllowedHeaders": ["*"],"AllowedMethods": ["PUT","POST","DELETE"],"AllowedOrigins": ["http://localhost:53762" //坑,本地测试,多端口尚未解决],"ExposeHeaders": ["ETag"]}]

坑4:jssdk

var accessKeyId = "{$:token.Credentials.AccessKeyId}";
var accessKeySecret = "{$:token.Credentials.SecretAccessKey}";
var sessionToken = "{$:token.Credentials.SessionToken}";//坑,要传
//坑,要传,特别是协议cors
var endpoint = `${location.protocol}//s3.cn-northwest-1.amazonaws.com.cn`;
var s3 = new AWS.S3({ accessKeyId: accessKeyId, secretAccessKey: accessKeySecret, sessionToken: sessionToken, region: 'cn-northwest-1', endpoint: endpoint, signatureVersion: 'v4' });//坑 signatureVersion不指定报错

坑5:Lambda

需要重点了解和会编写脚本,比如Nodejs phython等解释性语言。如果使用Java或.NET需要编译的语言,需要按照官方文档实现开发,和打包成.gz文件上传到Aws,Aws提供了编译语言对应的运行环境,需要注意环境支持的版本等信息。建议使用解释性语言,好处是只要Aws提供了对应的解释器,就可以在线编写代码进行测试,缺点是无法本地调试或逐语句调试。即使使用解释性语言,复杂业务逻辑或引用第三方包,还是需要本地开发并打包成.gz文件上传到Aws。
Nodejs 支持异步的话,务必使用await,否则异步操作不会执行,比如:

 await new Promise(function (resolve, reject) {const req = http.request(options, res => {console.log(`状态码: ${res.statusCode}`)res.on('data', d => {})});req.on('error', error => {console.error(error)});req.write(data);req.end();                         });

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

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

相关文章

【软件测试】学习笔记-测试覆盖率

测试覆盖率通常被用来衡量测试的充分性和完整性&#xff0c;从广义的角度来讲&#xff0c;测试覆盖率主要分为两大类&#xff0c;一类是面向项目的需求覆盖率&#xff0c;另一类是更偏向技术的代码覆盖率。 需求覆盖率 需求覆盖率是指测试对需求的覆盖程度&#xff0c;通常的做…

YOLOv5训练损失、精度、mAP绘图功能 | 支持多结果对比,多结果绘在一个图片(消融实验、科研必备)

一、本文介绍 本文给大家带来的是YOLOv5系列的绘图功能,我将向大家介绍YOLO系列的绘图功能。我们在进行实验时,经常需要比较多个结果,针对这一问题,我写了点代码来解决这个问题,它可以根据训练结果绘制损失(loss)和mAP(平均精度均值)的对比图。这个工具不仅支持多个文件…

本地引入Element UI后导致图标显示异常

引入方式 npm 安装 推荐使用 npm 的方式安装&#xff0c;它能更好地和 webpack 打包工具配合使用。 npm i element-ui -SCDN 目前可以通过 unpkg.com/element-ui 获取到最新版本的资源&#xff0c;在页面上引入 js 和 css 文件即可开始使用。 <!-- 引入样式 --> <…

脆弱的SSL加密算法漏洞原理以及修复方法

漏洞名称&#xff1a;弱加密算法、脆弱的加密算法、脆弱的SSL加密算法、openssl的FREAK Attack漏洞 漏洞描述&#xff1a;脆弱的SSL加密算法&#xff0c;是一种常见的漏洞&#xff0c;且至今仍有大量软件支持低强度的加密协议&#xff0c;包括部分版本的openssl。其实&#xf…

数据库攻防学习之Redis

Redis 0x01 redis学习 在渗透测试面试或者网络安全面试中可能会常问redis未授权等一些知识&#xff0c;那么什么是redis&#xff1f;redis就是个数据库&#xff0c;常见端口为6379&#xff0c;常见漏洞为未授权访问。 0x02 环境搭建 这里可以自己搭建一个redis环境&#xf…

[Angular] 笔记 23:Renderer2 - ElementRef 的生产版本

chatgpt: Renderer2 简介 在 Angular 中&#xff0c;Renderer2 是一个服务&#xff0c;用于处理 DOM 操作的抽象层。它提供了一种安全的方式来操作 DOM&#xff0c;同时与平台无关&#xff0c;有助于维护应用程序的跨浏览器兼容性和安全性。 Renderer2 的作用是在 Angular 组…

【电商项目实战】商品详情显示与Redis存储购物车信息

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《电商项目实战》。&#x1f3af;&#x1f3af; &am…

OceanBase入选Gartner®云数据库管理系统魔力象限“荣誉提及”

近日&#xff0c;全球IT市场研究和咨询公司Gartner发布最新报告《Magic Quadrant™ for Cloud Database Management Systems》&#xff08;全球云数据库管理系统魔力象限&#xff09;。全自研分布式数据库 OceanBase 入选“荣誉提及”&#xff0c;2022 年推出的云数据库 OB Clo…

redis 面试问题 (更新中 ing)

目录 reids 是做什么的为什么那么快有哪些使用场景redis有哪些 数据结构redis 有哪些底层数据结构为什么设计 sds一个 字符串 存储多大容量 stream为什么设计 streamstream 消费者消息丢失stream 消息私信问题 持久化机制redis 持久化机制&#xff0c;优缺点&#xff0c;怎么用…

模型 安索夫矩阵

本系列文章 主要是 分享模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。产品市场战略。 1 安索夫矩阵的应用 1.1 江小白的多样化经营策略 使用安索夫矩阵来分析江小白市场战略。具体如下&#xff1a; 根据安索夫矩阵&#xff0c;江小白的现有产品是其白酒产品&…

Mybatis之多表查询

目录 一、简介 1、使用嵌套查询: 2、使用多个 SQL 语句&#xff1a; 3、使用关联查询&#xff1a; 4、使用自定义映射查询&#xff1a; 二、业务场景 三、示例 1、一对一查询 2、一对多查询 一、简介 MyBatis 是一个优秀的持久层框架&#xff0c;它提供了强大的支持来执…

mycat 安装和水平分表

1.拉取需要创建docker版的mycat资源 # 下载对应的资源 git clone https://github.com/ruanjiayu/docker.mycat # 进入docker.mycat cd /home/cluster/mycat/docker.mycat # 构建镜像 docker-compose build 2.修改配置 vim config/mycat/schema.xml <?xml version"…

通信原理期末复习——计算大题(一)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

个人财务管理软件Money Pro mac功能特点

Money Pro mac是一款专为Mac用户设计的个人财务管理软件&#xff0c;具有全面的账户管理、智能的预算规划、强大的投资分析、丰富的报表和图表、安全的数据保护以及易于使用的界面设计等特点。 Money Pro mac功能和特点 全面的账户管理&#xff1a;支持多种账户类型&#xff0…

SpringBoot解决前后端分离跨域问题:状态码403拒绝访问

最近在写和同学一起做一个前后端分离的项目&#xff0c;今日开始对接口准备进行 登录注册 的时候发现前端在发起请求后&#xff0c;抓包发现后端返回了一个403的错误&#xff0c;解决了很久发现是【跨域问题】&#xff0c;第一次遇到&#xff0c;便作此记录✍ 异常描述 在后端…

iOS实时查看App运行日志

目录 一、设备连接 二、使用克魔助手查看日志 三、过滤我们自己App的日志 &#x1f4dd; 摘要&#xff1a; 本文介绍了如何在iOS iPhone设备上实时查看输出在console控制台的日志。通过克魔助手工具&#xff0c;我们可以连接手机并方便地筛选我们自己App的日志。 &#x1f4…

基于ssm的智慧社区电子商务系统+vue论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

如何在ArcGIS Pro中指定坐标系

在进行制图的时候&#xff0c;为了实现某些特定的效果&#xff0c;需要指定特定的坐标系&#xff0c;但是现有的数据可能不是所需要的坐标系&#xff0c;这时候就需要对现有的数据坐标系进行处理&#xff0c;这里为大家介绍一下ArcGIS Pro中指定坐标系的方法&#xff0c;希望能…

Transformer-MM-Explainability

two modalities are separated by the [SEP] token&#xff0c;the numbers in each attention module represent the Eq. number. E h _h h​ is the mean&#xff0c; ∇ \nabla ∇A : ∂ y t ∂ A {∂y_t}\over∂A ∂A∂yt​​for y t y_t yt​ which is the model’s out…

[每周一更]-(第51期):Go的调度器GMP

参考文献 https://learnku.com/articles/41728http://go.cyub.vip/gmp/gmp-model.html#g-m-phttps://blog.csdn.net/ByteDanceTech/article/details/129292683https://www.ququ123.top/2022/04/golang_gmp_principle/ 什么是GMP? GMP模型是Go语言并发模型的核心概念&#x…