阿里云迁移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(); });
相关文章:

阿里云迁移AWS视频点播技术攻坚
文章目录 🐷 背景🦥 简述🐥 Aws服务🦜 AWS CloudFormation🐞 问题🐉 落地方案🦉 Aws vs Aliyun🍄 避坑指南 🐷 背景 由于AWS整体成本略低于阿里云,公司决定将…...
Scrum敏捷认证CSM官方认证班Certified ScrumMaster - CSM认证班
课程简介 Scrum是目前运用最为广泛的敏捷开发方法,是一个轻量级的项目管理和产品研发管理框架,旨在最短时间内交付最大价值。根据2021年全球敏捷状态报告,Scrum及Scrum衍生方法的应用占比达到81%。 在企业的敏捷转型历程中,Scru…...
深度解析qt核心机制:信号槽的多线程行为与对象的线程依附性
对象的线程依附性 每一个学过C以及系统编程的程序员,对于变量会与特定线程有关联都会感到不可思议;在qt中所说的对象的线程依附性,只是针对继承自QObject的对象而言的;对象的线程依附性,并不是代表真的某个底层线程才…...

关于时间格式yyyy-M-d或yyyy-MM-d到yyyy-MM-dd的转换
工作时遇到前端传的时间格式是"2023-12-3 17:41:52",和"2023-1-1 17:41:52"但是我想要的是"2023-12-03 17:41:52"和"2023-01-01 17:41:52"。下面给大家分享几个解决方法 方法一: 找前端!让他改&…...

【Windows】之微软输入法配置小鹤双拼
前言 Windows 自带的输入法微软输入法本身就是个最简洁、最方便的输入法,不需要去安装多余的第三方输入法软件。同时,微软中文拼音输入法支持双拼输入法,但微软自带的双拼输入法不包含小鹤双拼方案的。所以,在这里将会讲解如何配置…...

【AI】使用Jan.ai在本地部署大模型开启AI对话(含通过huggingface下载大模型,实现大模型自由)
文章目录 前言一、Jan.ai是什么?二、下载大模型1. 找到大模型文件地址2. 下载大模型3. 修改model.json文件 三、使用Jan调用大模型进行对话总结 前言 2023年是AIGC元年。以后,每个人多少都会接触到GPT带来的变化。别人都在用,我们也不能落下…...

C++摸版(初阶)----函数模版与类模版
本专栏内容为:C学习专栏,分为初阶和进阶两部分。 通过本专栏的深入学习,你可以了解并掌握C。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:C 🚚代码仓库:小小unicorn的代码仓库&…...

Embedded-Project项目介绍
Embedded-Project项目介绍 Server后端项目后端启动连接数据库启动时可能遇到的问题架构介绍 web前端项目前端启动启动时可能遇到的问题架构介绍 前后端分离开发流程 项目地址: https://github.com/Catxiaobai/Embedded-Project Server后端项目 系统后端项目&#…...
golang 的那些花样
从 A Tour of Go 可以看到一些 Go 比较特殊的点 文章目录 变量声明时,类型放在后面Array 的引用 Slicereceiver 和 argumentbuilt-int特殊接口Error 变量声明时,类型放在后面 var i, j int 1, 2declaration-syntax Array 的引用 Slice slices-intro …...

如何设计企业级业务流程?学习华为的流程六级分类经验
业务流程管理(BPM)是一种系统化的方法,用于分析、设计、执行、监控和优化组织的业务流程,以实现预期的目标和价值。业务流程管理中,流程的分级方法有多种,常见的有以下几种: APQC的流程分级方法…...

视频智能分析支持摄像头异常位移检测,监测摄像机异常位移变化,保障监控状态
我们经常在生产场景中会遇到摄像头经过风吹日晒,或者异常的触碰,导致了角度或者位置的变化,这种情况下,如果不及时做出调整,会导致原本的监控条件被破坏,发生事件需要追溯的时候,查不到对应位置…...

C++ UTF-8与GBK字符的转换 —基于Linux 虚拟机 (iconv_open iconv)
1、UTF-8 和 GBK 的区别 GBK:通常简称 GB (“国标”汉语拼音首字母),GBK 包含全部中文字符。 UTF-8 :是一种国际化的编码方式,包含了世界上大部分的语种文字(简体中文字、繁体中文字、英文、…...

云原生十二问
一、什么是云原生? 云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。现代企业希望构建高度可扩展、灵活且具有弹性的应用程序,可以快速更新以满足客户需求。为此,他们使用现代工具和技术,这些工具和技术本质上支…...

K8Spod组件
一个pod能包含几个容器 一个pause容器(基础容器/父容器/根容器) 一个或者多个应用容器(业务容器) 通常一个Pod最好只包含一个应用容器,一个应用容器最好也只运行一个业务进程。 同一个Pod里的容器都是运行在同一个node节点上的,并且共享 net、…...
clickhouse-client INSERT CSV/TSV时跳过错误行
clickhouse-client INSERT CSV/TSV时跳过错误行 在使用clickhouse-client向ck中导入csv文件时,当csv中有个别行数据格式错误时,整个文件就插入失败了,经常会导致丢数据。 经过一番搜索,发现ck提供了两个参数可以跳过错误行&#x…...

直流稳压电源电路
一、稳压电源的技术指标及对稳压电源的要求 稳压电源的技术指标可以分为两大类:一类是特性指标,如输出电压、输出电滤及电压调节范围;另一类是质量指标,反映一个稳压电源的优劣,包括稳定度、等效内阻(输出电阻&#x…...
记录爬虫编写步骤
本文讲解 Python 爬虫实战案例:抓取百度贴吧(https://tieba.baidu.com/)页面,比如 Python爬虫吧、编程吧,只抓取贴吧的前 5 个页面即可。今天一个毕业学生问到一个问题:不清楚编写爬虫的步骤,不…...

SpringBoot配置Swagger2与Swagger3
swagger是什么? 在平时开发中,一个好的API文档可以减少大量的沟通成本,还可以帮助新加入项目的同事快速上手业务。大家都知道平时开发时,接口变化总是很多,有了变化就要去维护,也是一件比较头大的事情。尤…...
C/C++ 枚举
目录 枚举概述 枚举的使用 枚举的大小计算 枚举的优点 C语言中的自定义类型有:结构 位段 枚举 联合 枚举概述 枚举顾名思义就是一一列举,把可能的取值一一列举。 比如我们现实生活中:一周的星期一到星期日是有限的7天,…...

P12 音视频复合流——TS流讲解
前言 从本章开始我们将要学习嵌入式音视频的学习了 ,使用的瑞芯微的开发板 🎬 个人主页:ChenPi 🐻推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ 🔥 推荐专栏2: 《Linux C应用编程(概念类)_C…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...