前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[npm包管理器 ]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
转载文章
...体验。 · 动态电量管理 (Adaptive Battery) 无论用户们使用的是何种手机,电量一直都是他们最为关心问题。在 Android P 中,我们和 DeepMind 合作推出了一个全新功能,即动态电量管理 (Adaptive Battery),来优化各个应用的电量使用。 动态电量管理通过机器学习来管理用户们最关心的那些应用所能占用的系统资源。各个应用会被划分到四个不同的群组里,这些群组对系统资源调用有不同的限制,我们称之为 “应用待机群组 (App Standby buckets)”。随着用户的使用,应用会在这四个群组里切换,那些不在 “活跃 (active)” 组里的应用在包括任务 (jobs)、警报、网络以及高优先级的 Firebase Cloud Messages 等资源调用上会受到相应的限制。 如果您的应用已经针对 Doze, App Standby 和后台运行限制做过优化,那么它就应该已经能和动态电量管理完美配合。我们建议您在四个应用待机群组中都对自己的 app 进行测试,请阅读相关文档了解详情。 · App Actions 当用户想要做一个操作的时候,App Actions 会推荐能帮助他们完成这个操作的 app,而且这个推荐的功能会覆盖整个操作系统中的重要交互环节,比如启动器 (Launcher)、智能文本选择、Google Play、Google Search 应用,以及 Assistant。 App Actions 通过机器学习来分析用户最近的行为或使用场景,从而筛选出需要推荐的应用。由于这些推荐与用户当前想要做的事情高度关联,所以这套机制非常利于拓展新用户以及促活现有用户。 只需将您应用中的各个功能定义为语义意图 (semantic intent),便可以充分享受 App Actions 带来的好处。App Actions 中的意图和我们早些时候在 Google Assistant 上推出的语音对谈式动作 (Conversational Action) 是使用同一套通用意图分类,这个分类支持语音控制的音箱、智能屏幕、车载系统、电视、耳机等设备。由于不需要额外的 API 接口,所以只要用户的 Android 平台版本支持,App Actions 就可以正常使用了。 App Actions 很快就会面向开发者发布,如果您希望收到这方面的通知,请点击这里找到相关链接参与订阅。 · Slices 和 App Actions 一同到来的新功能还有 Slices,这个功能可以让您的应用以模块化、富交互的形式插入到多个使用场景中,比如 Google Search 和 Assistant。Slices 支持的交互包括 actions、开关、滑动条、滑动内容等等。 Slices 是让内容与用户联系的极佳方式,所以我们希望它可以在更多的场景中出现。除了在 Android P 上对这个功能进行了平台级别的整合外, Slices 的 API 和模板也加入到了 Android Jetpack 里。Android Jetpack 是我们全新打造的一套创建优秀应用的工具和库,通过 Android Jetpack,您制作的 Slices 能在 Kitkat (API 等级 19) 及更高版本上使用 —— 这覆盖了 95% 的已激活 Android 设备。我们也会定期更新 Slices 的模板来支持更多类型的场景和交互 (比如文本输入)。 请查阅上手指南以了解如何制作 Slices,使用 SliceViewer 工具查看您做好的 Slices。接下来,我们计划进一步拓展其使用场景,包括在其他 app 中展现您的 Slices。 · 通知智能回复 (Smart reply in notifications) 机器智能可以为用户体验带来非常积极的进化,Gmail 和 Inbox 里的智能回复功能已经成功地证明了这一点。在 Android P 中,通知消息也加入了智能回复功能,而且我们准备了 API 让您可以为用户带来更度身的使用感受。用来帮助您更轻松地在通知中生成回复的 ML Kit 很快就会到来,请 点击访问此网站 了解详情。 · 文本识别 (Text Classifier) 在 Android P 中,我们将识别文本的机器学习模型进行了扩展,使得它可以识别出诸如日期或航班号这样的信息,并通过 TextClassifier API 来让开发者使用到这些改进。我们还更新了 Linkify API 来利用文本识别的结果生成链接,并为用户提供了更多点击后的选项,从而让他们得以更快地进行下一步操作。当然,开发者也可以在给文本识别出来的信息添加链接时拥有更多的选项。智能 Linkify 在识别精准度以及速度上都有明显的提升。 这个模型现在正在通过 Google Play 进行更新,所以您的应用使用现有的 API 就可以享受到本次更新所带来的变化。在安装更新完的模型后,设备即可直接在本地识别文本里的各种信息,而且这些识别出来的信息只保存在您的手机上而不会通过网络流传出去。 请点击蓝色字体前往 “Android Developers 官方文档”查看详细说明 简洁 (Simplicity) 在 Android P,我们格外强调简洁,并据此改进 Android 的 UI 从而帮助用户们更流畅、更高效地完成操作。对开发者来说,简洁的系统则会帮助用户更容易查找、使用和管理您的应用。 · 全新系统导航 (New system navigation) 我们为 Android P 设计了全新的系统导航,只需使用下图中这个在所有界面中都能看到的小按钮,即可更轻松地访问手机主屏、概览页以及 Assistant。新导航系统也使多任务切换及发现关联应用变得更加简单。在概览页,用户可以拥有更大的视野来查看他们之前中断的操作,这自然也会让他们更容易找到并回到之前的应用中。概览页也提供了搜索、预测推荐应用以及上文提到的 App Actions,而且只需再多划一次即可进入所有应用的列表。 · 文字放大镜 (Text Magnifier) 在 Android P 中,我们加入了新的放大镜工具 (Magnifier widget),使选择文本和调整光标位置变得更加轻松。默认情况下,所有继承自 TextView 的类都会自动支持放大镜,但您也可以使用放大镜 API 将它添加到任何自定义的视图上,从而打造更多样化的体验。 · 后台限制 (Background restrictions) 用户可以更加简单地找到并管理那些在后台消耗电量的应用。通过 Android Vitals 积累下来的成果,Android 可以识别那些过度消耗电量的行为,如滥用唤醒锁定等。在 Android P 中,电池设置页面直接列出了这些过度消耗电量的应用,用户只需一次点击就可以限制它们在后台的活动。 一旦应用被限制,那么它的后台任务、警报、服务以及网络访问都会受限。想要避免被限制的话,请留意 Play Console 中的Android Vitals 控制面板,帮助您了解如何提高性能表现以及优化电量消耗。 后台限制能有效保护系统资源不被恶意消耗,从而确保开发者的应用在不同制造商的不同设备上也能拥有一个基础的合理的运行环境。虽然制造商可以在限制列表上额外添加限制的应用,但它们也必须在电池设置页面为用户开放这些限制的控制权。 我们添加了一个标准 API 来帮助应用知晓自己是否被限制,以及一个 ADB 命令来帮助开发者手动限制应用,从而进行测试。具体请参阅相关文档。接下来我们计划在 Play Console 的 Android Vitals 控制面板里添加一个统计数据,以展示应用受到限制的情况。 · 使用动态处理增强音频 (Enhanced audio with Dynamics Processing) Android P 在音频框架里加入了动态处理效果 (Dynamic Processing Effect) 来帮助开发者改善声音品质。通过动态处理,您可以分离出特定频率的声音,降低过大的音量,或者增强那些过小的音量。举例来说,即便说话者离麦克风较远,而且身处嘈杂或者被刺耳的各种环境音包围的地方,您的应用依然可以有效分离并增强他/她的细语。 动态处理 API 提供了多声场、多频段的动态处理效果,包括一个预均衡器、一个多频段压缩器,一个后均衡器以及一个串联的音量限制器。这样您就可以根据用户的喜好或者环境的变化来控制 Android 设备输出的声音。频段数量以及各个声场的开关都完全可控,大多数参数都支持实时控制,如增益、信号的压缩/释放 (attack/release) 时长,阈值等等。 请点击蓝色字体前往 “Android Developers 官方文档”查看详细说明 安全 (Security) · 用户识别提示 (Biometric prompt) Android P 为市面上涌现出来的各种用户识别机制在系统层面提供了统一的使用体验,应用们不再需要自行提供用户识别操作界面,而只需要使用统一的 BiometricPrompt API 即可。这套全新的 API 替代了 DP1 版本中的 FingerprintDialog API,且支持包括指纹识别 (包括屏幕下指纹识别)、面部识别以及虹膜识别,而且所有系统支持的用户识别需求都包含在一个 USE_BIOMETRIC 权限里。FingerprintManager 以及对应的 USE_FINGERPRINT 权限已经被废弃,请开发者尽快转用 BiometricPrompt。 · 受保护的确认操作 (Protected Confirmation) Android P 新增了受保护的确认操作 (Android Protected Confirmation),这个功能使用可信执行环境 (Trusted Execution Environment, TEE) 来确保一个显示出来的提示文本被真实用户确认。只有在用户确认之后,TEE 才会放行这个文本并可由应用去验证。 · 对私有密钥的增强保护 (Stronger protection for private keys) 我们添加了一个新的 KeyStore 类型,StrongBox。并提供对应的 API 来支持那些提供了防入侵硬件措施的设备,比如独立的 CPU,内存以及安全存储。您可以在 KeyGenParameterSpec 里决定您的密钥是否该交给 StrongBox 安全芯片来保存。 Android P Beta 为用户带来新版本的 Android 需要 Google、芯片供应商以及设备制造商和运营商的共同努力。这个过程中充满了技术挑战,并非一日之功 —— 为了让这个过程更加顺畅,去年我们启动了 Project Treble,并将其包含在 Android Oreo 中。我们与合作伙伴们一直在努力开发这个项目,也已经看到 Treble 所能带来的机遇。 我们宣布,以下 6 家顶级合作伙伴将和我们一起把 Android P Beta 带给全世界的用户,这些设备包括:索尼 Xperia XZ2, 小米 Mi Mix 2S, 诺基亚 7 Plus, Oppo R15 Pro, Vivo X21UD 和 X21, 以及 Essential PH‑1。此外,再加上 Pixel 2, Pixel 2 XL, Pixel 和 Pixel XL,我们希望来自世界各地的早期体验者以及开发者们都能通过这些设备体验到 Android P Beta。 您可查看今天推送的文章查阅支持 beta 体验的合作伙伴和 Pixel 设备清单,并能看到每款设备的详细配置说明。如果您使用 Pixel 设备,现在就可以加入 Android Beta program,然后自动获得最新的 Android P Beta。 马上开始在您喜欢的设备上体验 Android P Beta 吧,欢迎您向我们反馈意见和建议!并请继续关注 Project Treble 的最新动态。 确保 app 兼容 随着越来越多的用户开始体验 Android P Beta,是时候开始测试您 app 的兼容性,以尽早解决在测试中发现的问题并尽快发布更新。请查看迁移手册了解操作步骤以及 Android P 的时间推进表。 请从 Google Play 下载您的应用,并在运行 Android P Beta 的设备或模拟器上测试用户流程。确保您的应用体验良好,并正确处理 Android P 的行为变更。尤其注意动态电量管理、Wi-Fi 权限变化、后台调用摄像头以及传感器的限制、针对应用数据的 SELinux 政策、默认启用 TLS 的变化,以及 Build.SERIAL 限制。 · 公开 API 的兼容性 (Compatibility through public APIs) 针对非 SDK 接口的测试十分重要。正如我们之前所强调的,在 Android P 中,我们将逐渐收紧一些非 SDK 接口的使用,这也要求广大的开发者们,包括 Google 内部的应用团队,使用公开 API。 如果您的应用正在使用私有 Android API 或者库,您需要改为使用 Android SDK 或 NDK 公开的 API。我们在 DP1 里已经对使用私有接口的开发者发出了警告信息,从 Android P Beta 开始,调用非 SDK 接口将会报错 (部分被豁免的私有 API 除外) —— 也就是说您的应用将会遭遇异常,而不再只是警告了。 为了帮助您定位非 SDK API 的使用情况,我们在 StrictMode 里加入了两个新的方法。您可以使用 detectNonSdkApiUsage() 在应用通过反射或 JNI 调用非 SDK API 的时候收到警报,您还可以使用 permitNonSdkApiUsage() 来阻止 StrictMode 针对这些调用报错。这些方法都可助您了解应用调用非 SDK API 的情况,但请注意,即便调用的 API 暂时得到了豁免,最保险的做法依然是尽快放弃对它们的使用。 如果您确实遇到了公开 API 无法满足需求的情况,请立刻告知我们。更多详细内容请查看相关文档。 · 凹口屏测试 (Test with display cutout) 针对凹口屏测试您的应用也十分重要。现在您可以在运行 Android P Beta 的合作伙伴机型上测试,确保您的应用在凹口屏上表现良好。同时,您也可以在 Android P 设备的开发者选项里打开对凹口屏的模拟,对您的应用做相应测试。 体验 Android P 在准备好开发条件后,请深入了解 Android P 并学习可以在您的应用中使用到的全新功能和 API。为了帮助您更轻松地探索和使用新 API,请查阅 API 变化报告 (API 27->DP2, DP1->DP2) 以及 Android P API 文档。访问开发者预览版网站了解详情。 下载/更新 Android P 开发者预览版 SDK 和工具包至 Android Studio 3.1,或使用最新版本的 Android Studio 3.2。如果您手边没有 Android P Beta 设备 (或查看今天推送的次条文章),请使用 Android P 模拟器来运行和测试您的应用。 您的反馈一直都至关重要,我们欢迎您畅所欲言。如果您在开发或测试过程中遇到了问题,请在文章下方留言给我们。再次感谢大家一路以来的支持。 请点击蓝色字体前往 “Android Developers 官方文档”查看详细说明 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_34258782/article/details/87952581。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-04-10 18:19:36
338
转载
转载文章
...开发工具、迁移与运维管理和专有云等方面,阿里云都做的很不错。 2.2 证件照生成背景 传统做法:通常是人工进行P图,不仅费时费力,而且效果也很难保障,容易有瑕疵。 机器学习做法:通常利用边缘检测算法进行人物轮廓提取。 深度学习做法:通常使用分割算法进行人物分割。例如U-Net网络。 2.3 图像分割算法 《BiHand: Recovering Hand Mesh with Multi-stage Bisected Hourglass Networks》里的SeedNet网络是很经典的网络,它把分割任务转变成多个任务。作者的思想是:尽可能的通过多任务学习收拢语义,这样或许会分割的更好或姿态估计的更好。其实这个模型就是多阶段学习网络的一部分,作者想通过中间监督来提高网络的性能。 我提取bihand网络中的SeedNet与训练权重,进行分割结果展示如下 我是用的模型不是全程的,是第一阶段的。为了可视化出最好的效果,我把第一阶段也就是SeedNet网络的输出分别采用不同的方式可视化。 从左边数第一张图为原图,第二张图为sigmoid后利用plt.imshow(colored_mask, cmap=‘jet’)进行彩色映射。第三张图为网络输出的张量经过sigmoid后,二色分割图,阀闸值0.5。第四张为网络的直接输出,利用直接产生的张量图进行颜色映射。第五张为使用sigmoid处理张量后进行的颜色映射。第六张为使用sigmoid处理张量后进行0,1分割掩码映射。使用原模型和网络需要添加很多代码。下面为修改后的的代码: 下面为修改后的net_seedd代码: Copyright (c) Lixin YANG. All Rights Reserved.r"""Networks for heatmap estimation from RGB images using Hourglass Network"Stacked Hourglass Networks for Human Pose Estimation", Alejandro Newell, Kaiyu Yang, Jia Deng, ECCV 2016"""import numpy as npimport torchimport torch.nn as nnimport torch.nn.functional as Ffrom skimage import io,transform,utilfrom termcolor import colored, cprintfrom bihand.models.bases.bottleneck import BottleneckBlockfrom bihand.models.bases.hourglass import HourglassBisectedimport bihand.utils.func as funcimport matplotlib.pyplot as pltfrom bihand.utils import miscimport matplotlib.cm as cmdef color_mask(output_ok): 颜色映射cmap = plt.cm.get_cmap('jet') 将张量转换为numpy数组mask_array = output_ok.detach().numpy() 创建彩色图像cmap = cm.get_cmap('jet')colored_mask = cmap(mask_array)return colored_mask 可视化 plt.imshow(colored_mask, cmap='jet') plt.axis('off') plt.show()def two_color(mask_tensor): 将张量转换为numpy数组mask_array = mask_tensor.detach().numpy() 将0到1之间的值转换为二值化掩码threshold = 0.5 阈值,大于阈值的为白色,小于等于阈值的为黑色binary_mask = np.where(mask_array > threshold, 1, 0)return binary_mask 可视化 plt.imshow(binary_mask, cmap='gray') plt.axis('off') plt.show()class SeedNet(nn.Module):def __init__(self,nstacks=2,nblocks=1,njoints=21,block=BottleneckBlock,):super(SeedNet, self).__init__()self.njoints = njointsself.nstacks = nstacksself.in_planes = 64self.conv1 = nn.Conv2d(3, self.in_planes, kernel_size=7, stride=2, padding=3, bias=True)self.bn1 = nn.BatchNorm2d(self.in_planes)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(2, stride=2)self.layer1 = self._make_residual(block, nblocks, self.in_planes, 2self.in_planes) current self.in_planes is 64 2 = 128self.layer2 = self._make_residual(block, nblocks, self.in_planes, 2self.in_planes) current self.in_planes is 128 2 = 256self.layer3 = self._make_residual(block, nblocks, self.in_planes, self.in_planes)ch = self.in_planes 256hg2b, res1, res2, fc1, _fc1, fc2, _fc2= [],[],[],[],[],[],[]hm, _hm, mask, _mask = [], [], [], []for i in range(nstacks): 2hg2b.append(HourglassBisected(block, nblocks, ch, depth=4))res1.append(self._make_residual(block, nblocks, ch, ch))res2.append(self._make_residual(block, nblocks, ch, ch))fc1.append(self._make_fc(ch, ch))fc2.append(self._make_fc(ch, ch))hm.append(nn.Conv2d(ch, njoints, kernel_size=1, bias=True))mask.append(nn.Conv2d(ch, 1, kernel_size=1, bias=True))if i < nstacks-1:_fc1.append(nn.Conv2d(ch, ch, kernel_size=1, bias=False))_fc2.append(nn.Conv2d(ch, ch, kernel_size=1, bias=False))_hm.append(nn.Conv2d(njoints, ch, kernel_size=1, bias=False))_mask.append(nn.Conv2d(1, ch, kernel_size=1, bias=False))self.hg2b = nn.ModuleList(hg2b) hgs: hourglass stackself.res1 = nn.ModuleList(res1)self.fc1 = nn.ModuleList(fc1)self._fc1 = nn.ModuleList(_fc1)self.res2 = nn.ModuleList(res2)self.fc2 = nn.ModuleList(fc2)self._fc2 = nn.ModuleList(_fc2)self.hm = nn.ModuleList(hm)self._hm = nn.ModuleList(_hm)self.mask = nn.ModuleList(mask)self._mask = nn.ModuleList(_mask)def _make_fc(self, in_planes, out_planes):bn = nn.BatchNorm2d(in_planes)conv = nn.Conv2d(in_planes, out_planes, kernel_size=1, bias=False)return nn.Sequential(conv, bn, self.relu)def _make_residual(self, block, nblocks, in_planes, out_planes):layers = []layers.append( block( in_planes, out_planes) )self.in_planes = out_planesfor i in range(1, nblocks):layers.append(block( self.in_planes, out_planes))return nn.Sequential(layers)def forward(self, x):l_hm, l_mask, l_enc = [], [], []x = self.conv1(x) x: (N,64,128,128)x = self.bn1(x)x = self.relu(x)x = self.layer1(x)x = self.maxpool(x) x: (N,128,64,64)x = self.layer2(x)x = self.layer3(x)for i in range(self.nstacks): 2y_1, y_2, _ = self.hg2b[i](x)y_1 = self.res1[i](y_1)y_1 = self.fc1[i](y_1)est_hm = self.hm[i](y_1)l_hm.append(est_hm)y_2 = self.res2[i](y_2)y_2 = self.fc2[i](y_2)est_mask = self.mask[i](y_2)l_mask.append(est_mask)if i < self.nstacks-1:_fc1 = self._fc1[i](y_1)_hm = self._hm[i](est_hm)_fc2 = self._fc2[i](y_2)_mask = self._mask[i](est_mask)x = x + _fc1 + _fc2 + _hm + _maskl_enc.append(x)else:l_enc.append(x + y_1 + y_2)assert len(l_hm) == self.nstacksreturn l_hm, l_mask, l_encif __name__ == '__main__':a = torch.randn(10, 3, 256, 256) SeedNetmodel = SeedNet() output1,output2,output3 = SeedNetmodel(a) print(output1,output2,output3)total_params = sum(p.numel() for p in SeedNetmodel.parameters())/1000000print("Total parameters: ", total_params)pretrained_weights_path = 'E:/bihand/released_checkpoints/ckp_seednet_all.pth.tar'img_rgb_path=r"E:\FreiHAND\training\rgb\00000153.jpg"img=io.imread(img_rgb_path)resized_img = transform.resize(img, (256, 256), anti_aliasing=True)img256=util.img_as_ubyte(resized_img)plt.imshow(resized_img)plt.axis('off') 关闭坐标轴plt.show()''' implicit HWC -> CHW, 255 -> 1 '''img1 = func.to_tensor(img256).float() 转换为张量并且进行标准化处理''' 0-mean, 1 std, [0,1] -> [-0.5, 0.5] '''img2 = func.normalize(img1, [0.5, 0.5, 0.5], [1, 1, 1])img3 = torch.unsqueeze(img2, 0)ok=img3print(img.shape)SeedNetmodel = SeedNet()misc.load_checkpoint(SeedNetmodel, pretrained_weights_path)加载权重output1, output2, output3 = SeedNetmodel(img3)mask_tensor = torch.rand(1, 64, 64)output=output2[1] 1,1,64,64output_1=output[0] 1,64,64output_ok=torch.sigmoid(output_1[0])output_real=output_1[0].detach().numpy()直接产生的张量图color_mask=color_mask(output_ok) 显示彩色分割图two_color=two_color(output_ok)显示黑白分割图see=output_ok.detach().numpy() 使用Matplotlib库显示分割掩码 plt.imshow(see, cmap='gray') plt.axis('off') plt.show() print(output1, output2, output3)images = [resized_img, color_mask, two_color,output_real,see,see]rows = 1cols = 4 创建子图并展示图像fig, axes = plt.subplots(1, 6, figsize=(30, 5)) 遍历图像列表,并在每个子图中显示图像for i, image in enumerate(images):ax = axes[i] if cols > 1 else axes 如果只有一列,则直接使用axesif i ==5:ax.imshow(image, cmap='gray')else:ax.imshow(image)ax.imshowax.axis('off') 调整子图之间的间距plt.subplots_adjust(wspace=0.1, hspace=0.1) 展示图像plt.show() 上述的代码文件是在bihand/models/net_seed.py中,全部代码链接在https://github.com/lixiny/bihand。 把bihand/models/net_seed.p中的代码修改为我提供的代码即可使用作者训练好的模型和进行各种可视化。(预训练模型根据作者代码提示下载) 3.调用阿里云API进行证件照生成实例 3.1 准备工作 1.找到接口 进入下面链接即可快速访问 link 2.购买试用包 3.查看APPcode 4.下载代码 5.参数说明 3.2 实验代码 !/usr/bin/python encoding: utf-8"""===========================证件照制作接口==========================="""import requestsimport jsonimport base64import hashlibclass Idphoto:def __init__(self, appcode, timeout=7):self.appcode = appcodeself.timeout = timeoutself.make_idphoto_url = 'https://idp2.market.alicloudapi.com/idphoto/make'self.headers = {'Authorization': 'APPCODE ' + appcode,}def get_md5_data(self, body):"""md5加密:param body_json::return:"""md5lib = hashlib.md5()md5lib.update(body.encode("utf-8"))body_md5 = md5lib.digest()body_md5 = base64.b64encode(body_md5)return body_md5def get_photo_base64(self, file_path):with open(file_path, 'rb') as fp:photo_base64 = base64.b64encode(fp.read())photo_base64 = photo_base64.decode('utf8')return photo_base64def aiseg_request(self, url, data, headers):resp = requests.post(url=url, data=data, headers=headers, timeout=self.timeout)res = {"status_code": resp.status_code}try:res["data"] = json.loads(resp.text)return resexcept Exception as e:print(e)def make_idphoto(self, file_path, bk, spec="2"):"""证件照制作接口:param file_path::param bk::param spec::return:"""photo_base64 = self.get_photo_base64(file_path)body_json = {"photo": photo_base64,"bk": bk,"with_photo_key": 1,"spec": spec,"type": "jpg"}body = json.dumps(body_json)body_md5 = self.get_md5_data(body=body)self.headers.update({'Content-MD5': body_md5})data = self.aiseg_request(url=self.make_idphoto_url, data=body, headers=self.headers)return dataif __name__ == "__main__":file_path = "图片地址"idphoto = Idphoto(appcode="你的appcode")d = idphoto.make_idphoto(file_path, "red", "2")print(d) 3.3 实验结果与分析 原图片 背景为红色生成的证件照 背景为蓝色生成的证件照 另外尝试了使用柴犬照片做实验,也生成了证件照 原图 背景为红色生成的证件照 参考(可供参考的链接和引用文献) 1.参考:BiHand: Recovering Hand Mesh with Multi-stage Bisected Hourglass Networks(BMVC2020) 论文链接:https://arxiv.org/pdf/2008.05079.pdf 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_37758063/article/details/131128967。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-11 23:36:51
131
转载
转载文章
...or的结合,内置坏块管理系统;价高 USB四线接口简单介绍 开发电脑选择:核心越多越好,主频越高越好----->编译工程快 设置ubuntu系统ip的方法:右上角找到设置图标,选择network,点齿轮图标号,在ipv4下面设置地址192.168.1.x,子网掩码255.255.255.0,网关192.168.1.1(必须要使windows,ubuntu,开发板处于同一网段,能互相ping通) U盘连接到主机和UBUNTU相互转换:虚拟机右下角,右键连接or断开 shell常用指令 ls -a:显示所有目录,文件夹,隐藏文件/目录 ls -l:显示文件的权限、修改时间等 ls -al:上面两个结合 ls 目录:显示该目录下的文件 – cd /:进入linux根目录 cd ~:/home/jl – uname :查看系统信息 uname -a :查看全部系统信息 – cat 文件名:显示某文件内容 – sudo :临时切换root用户 sudo apt-get install 软件名 :装某软件 sudo su:直接切换root用户(少用) sudo su jl:切换回普通用户 – touch 文件名:创建文件 rm -r 目录/文件:删除文件/目录及它包含的所有内容 rm -f 文件:直接删除,无需确认 rm -i 文件:删除文件,会逐一询问是否删除 rmdir 目录:专门删除目录 mv :可以用来移动文件/目录,也可以用来重命名 – ifconfig:显示网络配置信息(lo:本地回环测试) ifconfig -a:显示所有网卡(上面只显示工作的,本条显示所有工作和未工作的) ifconfig eth0 up:打开eth0这个网卡 ifconfig eth0 down:关闭eth0这个网卡(0一般要sudo来执行) ifconfig eth0 你想设置的地址:重设eth0的ip地址 – 命令 --help:看看这个命令的帮助信息 reboot:重启 – sync:数据同步写入磁盘命令(一般来说,用户写的内容先保存在一个缓冲区,系统是隔一定时间像磁盘写入缓冲区内写入磁盘),用sync立刻写入 grep ”“ -i :搜索时忽略大小写 grep 默认是匹配字符, -w 选项默认匹配一个单词 例如我想匹配 “like”, 不加 -w 就会匹配到 “liker”, 加 -w 就不会匹配到 du 目录/文件 -sh : 查看某一文件/目录的大小,也可以到一个目录下du -sh,查看这个目录的大小 目录下使用du -sh 查看目录总的大小 du 文件名 -sh 查看指定文件的大小 df:检查linux服务器的文件系统磁盘空间占用情况,默认以kb为单位 gedit 文件:使用gedit软件打开一个文件(类似于windows下面的记事本) ps:查看您当前系统有哪些进程,ubuntu(多用户)下是ps -aux,嵌入式linux(单用户)下面是ps top:进程实时运行状态查询 file 文件名:查看文件类型 ubuntu的fs cd / :根目录,一切都是从根目录发散开来的 /bin:存放二进制可执行文件,比如一些命令 /boot:ubuntu的内核与启动文件 /cdrom:有光盘是存放光盘文件 /dev:存放设备驱动文件 /etc:存放配置文件,如账号和密码文件(加密后的) /home:系统默认的用户主文件夹 /lib:存放库文件 /lib64:存放库文件,. so时linux下面的动态库文件 /media:存放可插拔设备,如sd,u盘就是挂载到这个文件下面 /mnt:用户可使用的挂载点,和media类似,可以手动让可插拔设备挂载到/mnt /opt:可选的文件和程序存放目录,给第三方软件放置的目录 /proc:存放系统的运行信息,实在内存上的不是在flash上,如cat /proc/cpuinfo /root:系统管理员目录,root用户才能访问的文件 /sbin:和bin类似,存放一些二进制可执行文件,sbin下面一般是系统开机过程中所需要的命令 /srv:服务相关的目录,如网络服务 /sys:记录内核信息,是虚拟文件系统 /tmp:临时目录 /usr:不是user的缩写,而是UNIX Software Resource的缩写,存放系统用户有关的文件,占很大空间 /var:存放变化的文件,如日志文件 – 移植就是移植上面这些文件 磁盘管理 linux开发一定要选用FAT32格式的U盘或者SD卡 u盘在/dev中的名字是sd,要确定是哪个,拔了看少了哪个。就是哪个 /dev/sdb表示U盘,/dev/sdb1表示U盘的第一个分区,一般U盘 sd卡只有一个分区 df:显示linux系统的磁盘占用情况 在一个目录里使用du -sh:查看这个目录里面所有内容所占用的资源 du 文件名 -sh:一般用来看单个文件/目录的大小 du -h --max-depth=n:显示n级目录的大小 – 磁盘的挂载与取消挂载: mount 和 umount sudo mount /dev/sdb1 /media/jl/udisk sudo umount /media/jl/u盘名 (-f 强制取消挂载),如果u盘正在使用,如被另一个终端打开,那么该指令无效 mount挂载后中文显示乱码的解决方法 sudo mount -o iocharset=utf8 /dev/sdb1 udisk – 磁盘的分区和格式化 sudo fdisk -l /dev/sdb 查看所有分区信息(–help查看别的用法) sudo fdisk /dev/sdb1 ----> m ( 进入帮助 ) ----> d 删除该分区 ----> wq 保存并退出 mkfs -t vfat /dev/sdb1 mkfs -t vfat /dev/sdb2 mkfs -t vfat /dev/sdb3 给分区1,2,3分别格式化,完成后能在图形界面看见三个u盘图标 格式化u盘之前一定要先卸载u盘已经挂载的系统。 – 压缩和解压缩 linux下常用的压缩扩展名: .tar .tar.bz2 .tar.gz 后两个linux常用 windows下面用7zip软件 右键选中文件,选择7zip,添加到压缩包,压缩格式选择tar,仅存储 生成tar文件,这里只是打包,没有压缩 右键上面的tar文件,选择7zip,添加到压缩包,压缩格式选择bzip2,确定 生成.tar.bz2文件,把它放到ubuntu解压 ubuntu也支持解压.tar和.zip,但后面两个常用 – ubuntu下面的压缩工具时gzip 压缩文件 gzip 文件名:压缩文件,变成 原文件名.gz,原来的文件就不见了 解压缩文件 gzip -d .gz:还原 文件 gzip -r 目录:递归,将该目录里的各个文件压缩,不提供打包服务 – bzip2工具负责压缩和解压缩.bz2格式的压缩包 bzip2 -z 文件名,压缩成 文件名.bz2 bzip2 -d 文件名.bz2,解压缩成 文件名 bzip2不能压缩/解压缩 目录 – 打包工具 tar 常用参数 -f:使用归档文件(必须要在所有选项后面) -c:创建一个新归档 -x:从归档中解出文件 -j:使用bzip2压缩格式 -z:使用gzip压缩格式 -v:打印出命令执行过程 如以bzip2格式压缩,打包 tar -vcjf 目录名.tar.bz2 目录名 如将上面的压缩包解包 tar -vxjf 目录名.tar.bz2 – 其他压缩工具 rar工具 sudo apt-get install rar(用dhcp连不上阿里云的镜像) rar a test.rar test 把test压缩成test.rar rar x test.rar 把test.rar解压缩成test – zip工具 压缩 zip -rv test.zip test 解压缩 unzip test.zip – ubuntu的用户和用户组 linux是多用户的os,不同的用户有不同的权限,可以查看和操作不同的文件 有三种用户 1、初次用户 2、root用户 3、普通用户 root用户可以创建普通用户 linux用户记录在/etc/passwd这个文件内 linux用户密码记录在/etc/shadow这个文件内,不是以明文记录的 每个用户都有一个id,叫做UID – linux用户组 为了方便管理,将用户进行分组,每个用户可以属于多个组 可以设置非本组人员不能访问一些文件 用户和用户组的存在就是为了控制文件的访问权限的 每个用户组都有一个ID,叫做GID 用户组信息存储在/etc/group中 passwd 用户名:修改该用户的密码 – ubuntu文件权限 ls -al 文件名 如以b开头: -brwx - rwx - rwx -:b表示 块文件,设备文件里面可供存储的周边设备 以d开头是目录 以b是块设备文件 以-开头是普通文件 以 l 开头表示软连接文件 以c开头是设备文件里的串行端口设备 -rwx - rwx - rwx -:用户权限,用户组内其他成员,其它组用户 数字 1 表示链接数,包括软链接和硬链接 第三列 jl 表示文件的拥有者 第四列 jl 表示文件的用户组 第五列 3517 表示这个文件的大小,单位是字节 ls -l 显示的文件大小单位是字节 ls -lh 现实的文件大小单位是 M / G 第六七八列是最近修改时间 最后一列是文件名 – 修改文件权限命令 chmod 777 文件名 修改文件所属用户 sudo chown root 文件 修改文件用户组 sudo chown .root 文件 同时修改文件用户和用户组 sudo chown jl.jl 文件 修改目录的用户/用户组 sudo chown -r jl.jl 目录( root.root ) – linux连接文件 1、硬连接 2、符号连接(软连接) linux有两种连接文件,软连接/符号连接,硬连接 符号连接类似于windows下面的快捷方式 硬连接通过文件系统的inode连接来产生新文件名,而不是产生新文件 inode:记录文件属性,一个文件对应一个inode, inode相当于文件ID 查找文件要先找到inode,然后才能读到文件内容 – ln 命令用于创建连接文件 ln 【选项】源文件 目标文件 不加选项就是默认创建硬连接 -s 创建软连接 -f 强制创建连接文件,如果目标存在,就先删掉目标文件,再创建连接文件 – 硬连接:多个文件都指向同一个inode 具有向inode的多个文件互为硬连接文件,创建硬连接相当于文件实体多了入口 只有删除了源文件、和它所有的硬连接文件,晚间实体才会被删除 可以给文件创建硬连接来防止文件误删除 改了源文件还是硬连接文件,另一个文件的数据都会被改变 硬连接不能跨文件系统(另一个格式的u盘中的文件) 硬连接不能连接到目录 出于以上原因,硬连接不常用 ls -li:此时第一列显示的就是每个文件的inode – 软连接/符号连接 类似windows下面的快捷方式 使用较多 软连接相当于串联里一个独立的文件,该文件会让数据读取指向它连接的文件 ln -s 源文件 目标文件 特点: 可以连接到目录 可以跨文件系统 删除源文件,软连接文件也打不开了 软连接文件通过 “ -> ” 来指示具体的连接文件(ls -l) 创建软连接的时候,源文件一定要使用绝对路径给出,(硬连接无此要求) 软连接文件直接用cp复制到别的目录下,软连接文件就会变成实体文件,就算你把源文件删掉,该文件还是有效 正确的复制、移动软连接的用法是:cp -d 如果不用绝对路径,cp -d 软连接文件到别的目录,该软连接文件就会变红,失效 如果用了绝对路径,cp -d 软连接文件到别的目录,该软连接文件还是有效的,还是软连接文件 不用绝对路径,一拷贝就会出问题 – 软连接一个目录,也是可以用cp -d复制到其他位置的 – gedit 是基于图形界面的 vim有三种模式: 1、一般模式:默认模式,用vim打开一个文件就自动进入这个模式 2、编辑模式:按 i,a等进入,按esc回到一般模式 3、命令行/底行模式:在一般模式下输入:/ ?可进入命令行模式 ,按esc回到一般模式 一般模式下,dd删除光标所在的一整行; ndd,删除掉光标所在行和下面的一共n行 点 . 重复上一个操作 yy复制光标所在行 小p复制到光标下一行 大p复制到光标上一行n nyy复制光标所在往下n行 设置vim里的tab是四个空格:在/etc/vim/vimrc里面添加:set ts=4 设置vim中显示行号:在上面那个文件里添加:set nu – vscode是编辑器 gcc能编译汇编,c,cpp 电脑上的ubuntu自带的gcc用来编译x86架构的程序,而嵌入式设备的code要用针对于该芯片架构如arm的gcc编译器,又叫做交叉编译器(在一种架构的电脑上编译成另一种架构的代码) gcc -c 源文件:只编译不链接,编译成.o文件 -o 输出文件名( 默认名是 .out ) -O 对程序进行优化编译,这样产生的可执行文件执行效率更高 -O2:比-O幅度更大的优化,但编译速度会很慢 -v:显示编译的过程 gcc main.c 输出main.out的可执行文件 预处理 --> 编译 --> 汇编 --> 链接 – makefile里第一个目标默认是终极目标 其他目标的顺序可以变 makefile中的变量都是字符串 变量的引用方法 : $ ( 变量名 ) – Makefile中执行shell命令默认会把命令本身打印出来 如果在shell命令前加 @ ,那么shell’命令本身就不会被打印 – 赋值符:= 变量的有效值取决于他最后一次被赋值的值 : = 赋值时右边的值只是用前面已经定义好的,不会使用后面的 ?= 如果左边的前面没有被赋值,那么在这里赋值,佛则就用前面的赋值 + = 左边前面已经复制了一些字串,在这里添加右边的内容,用空格隔开 – 模式规则 % . o : % . c %在这里意思是通配符,只能用于模式规则 依赖中 % 的内容取决于目标 % 的内容 – CFLAGS:指定头文件的位置 LDFLAGS:用于优化参数,指定库文件的位置 LIBS:告诉链接器要链接哪些库文件 VPATH:特殊变量,指定源文件的位置,冒号隔开,按序查找源文件 vpath:关键字,三种模式,指定、清除 – 自动化变量 $ @ 规则中的目标集合 $ % 当目标是函数库的时候,表示规则中的目标成员名 $ < 依赖文件集合中的第一个文件,如果依赖文件是以 % 定义的,那么 $ < 就是符合模式的一系列文件的集合 $ ? 所有比目标新的依赖文件的集合,以空格分开 $ ^ 所有依赖文件的集合,用空格分开,如果有重复的依赖文件,只保留一次 $ + 和 $ ^ 类似,但有多少重复文件都会保留 $ 表明目标模式中 % 及其以前的部分 如果目标是 test/a.test.c,目标模式是 a.%.c,那么 $ 就表示 test/a.test – 常用的是 $@ , $< , $^ – Makefile的伪目标 不生成目标文件,只是执行它下面的命令 如果被错认为是文件,由于伪目标一般没有依赖,那么目标就被认为是最新的,那么它下面的命令就不会执行 。 如果目录下有同名文件,伪目标错认为是该文件,由于没有依赖,伪目标下面的指令不会被执行 伪目标声明方法 .PHONY : clean 那么就算目录下有伪目标同名文件,伪目标也同样会执行 – 条件判断 ifeq ifneq ifdef ifndef – makefile函数使用 shell脚本 类似于windoes的批处理文件 将连续执行的命令写成一个文件 shell脚本可以提供数组,循环,条件判断等功能 开头必须是:!/bin/bash 表示使用bash 脚本的扩展名:.sh – 交互式shell 有输入有输出 输入:read 第三行 name在这里作为变量,read输入这个变量 下一行使用这个变量直接是 $name,不用像 Makefile 里面那样子加括号 read -p “读取前你想打印的内容” 变量1 变量2 变量3… – 数值计算 第五行等于号两边不能有空格 右边计算的时候是 $( ( ) ),注意要两个括号 – test 测试命令 文件状态查询,字符、数字比较 && cmd1 && cmd2 当cmd1执行完并且正确,那么cmd2也执行 当cmd2执行完并且错误,那么cmd2不执行 || cmd1 || cmd2 当cmd1执行完并且正确,那么cmd2不执行 当cmd2执行完并且错误,那么cmd2也执行 查看一个文件是否存在 – 测试两个字符串是否相等 ==两边必须要有空格,如果不加空格,test这句就一直是对的。 – 中括号判断符 [ ] 作用和test类似 里面只能输入 == 或者 != 四个箭头所指必须用空格隔开 而且如果变量是字符串的话,一定要加双引号 – 默认变量 $0——shell脚本本身的命令 $——最后一个参数的标号(1,2,3,4…) $@——表示 $1 , $2 , $3 … $1 $2 $3 – shell 脚本的条件判断 if [ 条件判断 ];then //do something fi 红点处都要加空格 exit 0——表示退出 – if 条件判断;then //do something elif 条件判断;them //do something else //do something fi 红线处要加空格 – case 语句 case $var in “第一个变量的内容”) //do something ;; “第二个变量的内容”) // do something ;; . . . “第n个变量的内容”) //do something ;; esac 不能用 “”,否则就不是通配符的意思,而是表示字符 – shell 脚本函数 function fname(){ //函数代码段 } 其中function可以写也可以不写 调用函数的时候不要加括号 shell 脚本函数传参方式 – shell 循环 while[条件] //括号内的状态是判断式 do //循环代码段 done – until [条件] do //循环代码段 done – for循环,使用该循环可以知道有循环次数 for var con1 con2 con3 … … do //循环代码段 done – for 循环数值处理 for((初始值;限制值;执行步长)) do //循环代码段 done – 红点处必须要加空格!! loop 环 – – 注意变量有的地方用了 $ ,有的地方不需要 $ 这里的赋值号两边都不用加 空格 $(())数值运算 本篇文章为转载内容。原文链接:https://blog.csdn.net/engineer0/article/details/107965908。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-23 17:18:30
79
转载
转载文章
...原因是三个关键因素让管理层下定决心上云: 1) 脉冲计算:从技术架构配套业务发展分析,网络订餐业务具有明显的“脉冲计算”特征,在每日上午10:00至13:00、晚间16:00至19:00业务高峰值出现,而其他时间则业务量很低,暑假是业务高峰季,2016年5.17大促,饿了么第一次做“秒杀”,一秒订单15000笔,巨大的波峰波谷计算差异,引发了自建数据中心容量不可调和的两难处境,如果大规模投入服务器满足6小时的高峰业务量,则其余18个小时的业务低谷计算资源闲置,若满足平均业务量,则无法跟上业务快速发展节奏,落后于竞争对手;搞电商大促时,计算资源投入巨大,大促之后计算峰值下降,采用自建机房利用率仅10%,所以技术团队摸索出用云计算扛营销大促峰值的新模式,采用混合云架构满足 “潮汐业务”峰值计算,阿里云海量云计算资源弹性随需满足巨大的脉冲计算力缺口,这与每年“双11” 淘宝引入阿里云形成全球最大混合云架构具有异曲同工的创新价值。 2) 数据量爆炸:伴随饿了么近五年业务量呈几何级数的爆发式发展,数据量增速更加令人吃惊,是业务量增速的5倍,每日增量数据接近100TB,2015年短短2个月内业务量增长10倍,数据量增长了50倍,上海主生产机房不堪重负。30GB的DDoS攻击对业务系统造成较大风险,上云成为承载大数据、抗网络攻击的好方法。 3) 高可用性挑战:众所周知,IDC自建系统运维要承担从底层硬件到上层应用的“全栈运维”运营能力与维修能力,当2015年夏天上海数据中心故障发生,主核心交换机宕机时,备核心交换机Bug同时被触发,从事故发生到硬件厂商携维修设备打车赶往现场维修的整个过程中,饥饿的消费者无法订餐吃饭,技术团队第一次经历业务中断而束手无策,才下定决心大笔投入混合云灾备的建设,“吃一堑,长一智”,持续向淘宝学习电商云生产与灾备架构,以自动化运维替代人肉运维,从灾备向多活演进,成为饿了么企业架构转型的必经之路。 4) 大数据精益运营:不论网络打车还是网络订餐,共享服务平台脱颖而出的关键成功要素是智能调度算法,以大数据训练算法提升调度效率,饿了么在高峰时段内让百万“骑士”(送餐快递员)完成更多订单是算法持续优化的目标,而这背后隐藏着诸多复杂因素,包括考虑餐厅、骑士、消费者三者的实时动态位置关系,把新订单插入现有“骑士”的行进路线中,估计每家餐厅出餐时间,每个骑手的行进速度、道路熟悉程度各不相同,新老消费者获客成本、高价低价订单的优先级皆不相同。种种考量因素合并到一起,对于人类调度员来说,每天中午和晚上的高峰都是巨大的挑战。以上海商城路配送站为例,一个调度员每6秒钟就要调度1单,他需要考虑骑手已有订单量、路线熟悉度等。因此可以说,这份工作已经完全不适合人类。但对人工智能而言,阿里云ET则非常擅长处理这类超复杂、大规模、实时性要求高的“非人”问题。 饿了么是中国最大的在线外卖和即时配送平台,日订单量900万单、180万骑手、100万家餐饮店,既是史无前例的计算存储挑战,又是人无我有的战略发展机遇。饿了么携手阿里云人工智能团队,通过海量数据训练优化全球最大实时智能调度系统。在基础架构层,云计算解决弹性支撑业务量波动的基础生存问题,在数据智能层,利用大数据训练核心调度算法、提升餐饮店的商业价值,才是业务决胜的“技术神器”。 在针对大数据资源的“专家+机器”运营分析中,不断发现新的特征: 1) 区域差异性:饿了么与阿里云联合研发小组测试中发现有2个配送站点出现严重超时问题。后来才知道:2个站点均在成都,当地人民喜欢早、中餐一起吃,高峰从11点就开始了。习惯了北上广节奏的ET到成都就懵了。据阿里云人工智能专家闵万里分析:“不存在一套通用的算法可以适配所有站点,所以我们需要让ET自己学习或者向人类运营专家请教当地的风土人情、饮食习惯”。除此之外,饿了么覆盖的餐厅不仅有高大上的连锁店,还有大街小巷的各类难以琢磨的特色小吃,难度是其他智能调度业务的数倍。 2) 复杂路径规划:吃一口热饭有多难?送餐路径规划比驾车出行路径规划难度更高,要考虑“骑士”地图熟悉程度、天气状况、拼单效率、送餐顺序、时间对客户满意度影响、送达写字楼电梯等待时间等各种实际情况,究竟ET是如何实现智能派单并确保效率最优的呢?简单来说,ET会将配送站新接订单插入到每个骑手已有的任务中,重新规划一轮最短配送路径,对比哪个骑手新增时间最短。为了能够准确预估新增时间,ET需要知道全国100万家餐厅的出餐速度、超过180万骑手各自的骑行速度、每个顾客坐电梯下楼取餐的时间。一般来说,餐厅出餐等待时间占到了整个送餐时间的三分之一。ET要想提高骑手效率,必须准确预估出餐时间以减少骑手等待,但又不能让餐等人,最后饭凉了。饿了么旗下蜂鸟配送“准时达”服务单均配送时长缩短至30分钟以内。 3) 天气特殊影响:天气等环境因素对送餐响应时间影响显著,要想计算骑手的送餐路程时间,ET需要知道每个骑手在不同区域、不同天气下的送餐速度。如果北京雾霾,ET能看见吗?双方研发团队为ET内置了恶劣天气的算法模型。通常情况下,每逢恶劣天气,外卖订单将出现大涨,对应的餐厅出餐速度和骑手骑行速度都将受到影响,这些ET都会考虑在内。如果顾客在下雪天点个火锅呢?ET也知道,将自动识别其为大单,锁定某一个骑手专门完成配送。 4) 餐饮营销顾问:饿了么整体业务涉及C端(消费者)、B端(餐饮商户)、D端(物流配送)、BD端(地推营销),以往区域业务开拓考核新店数量,现在会重点关注餐饮外卖“健康度”,对于营业额忽高忽低、在线排名变化的餐饮店,都需要BD专家根据大数据帮助餐饮店经营者找出原因并给出解决建议,避免新店外卖刚开始就淹没在区域竞争中,销量平平的新店会离开平台,通过机器学习把餐饮运营专家的经验、以及人看不到的隐含规律固化下来,以数据决策来发现餐饮店经营问题、产品差异定位,让餐饮商户尝到甜头,才愿意继续经营。举个例子,饿了么员工都喜欢楼下一家鸡排店的午餐,但大数据发现这家店的外卖营收并不如实体店那么火爆,9元“鸡排+酸梅汁”是所有人都喜欢的爆款产品,可为什么同样菜品遭遇“线下火、线上冷”呢?数据预警后,BD顾问指出线上外卖鸡排产品没有写明“含免费酸梅汁一杯”的关键促销内容,导致大多数外卖消费者订一份鸡排一杯酸梅汁,却收到一份鸡排两杯酸梅汁,体验自然不好。 饿了么是数据驱动、智能算法调度的自动化生活服务平台,通过O2O数据的在线实时分析,与阿里云人工智能团队不断改进算法,以“全局最优”取代“局部最优”,保证平台上所有餐饮商户都能享受到数据智能的科技红利。 “上云用数”的外部价值诸多,从饿了么内部反馈来看,上云不仅没有让运维团队失去价值,反而带来了“云原生应用”(Cloud Native Application)、“云上多活”、“CDN云端压测”、“安全风控一体化”等创新路径与方案,通过敏捷基础设施(IaaS)、微服务架构(PaaS和SaaS)、持续交付管理、DevOps等云最佳实践,摆脱“人肉”支撑的种种困境,进而实现更快的上线速度、细致的故障探测和发现、故障时能自动隔离、故障时能够自动恢复、方便的水平扩容。饿了么CTO张雪峰先生说:“互联网平台型组织,业务量涨数倍,企业人数稳定降低,才是技术驱动的正确商业模式。” 在不久的将来,你每天订餐、出行、娱乐、工作留下的大数据,会“驯养”出无处不在、无所不能的智能机器人管家,家庭助理帮你点菜,无人机为你送餐,聊天机器人接受你的投诉……当然这个无比美妙的“未来世界”背后,皆有阿里云的数据智能母体“ET”。 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_34126557/article/details/90592502。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-01-31 14:48:26
343
转载
转载文章
...效利用,通过智能指针管理资源生命周期,确保了内存安全,减少了潜在的内存泄漏风险。 此外,随着C++17及后续标准的发布,类模板与友元机制也得到了进一步增强和完善。比如,在C++20中引入了模块(Modules)特性,使得类模板的编译速度大大提高,同时增强了类型安全性,对于大型项目的组织结构优化具有重要意义。友元在现代C++设计模式中仍然发挥着不可替代的作用,特别是在实现组件间深度交互时,如游戏引擎中不同系统间的紧密协作,往往借助友元关系来突破封装限制,实现高效的底层数据访问。 另外,对于对象数组、对象指针以及静态成员的讨论,实则指向了更为复杂的内存管理和对象生命周期问题。近期一篇来自ACM Queue的文章《深入剖析C++内存模型》对此做了深度解读,并探讨了在多线程环境下的同步控制和内存一致性问题,这对于理解并有效利用C++进行高性能并发编程至关重要。 总之,掌握好本文所述的基础知识是至关重要的,而与时俱进地了解最新实践和技术趋势,将有助于我们更高效、安全地运用C++进行软件开发,解决实际工程中的复杂问题。
2024-01-29 12:38:23
544
转载
转载文章
...问技术,问了很多技术管理方面的问题 自我介绍 项目介绍 怎么管理项目成员 当意见不一致时,如何沟通并说服开发成员,并举个例子 怎么保证项目的进度 数据库的索引原理 非聚簇索引和聚簇索引 索引的使用注意事项 联合索引 从底层解释最左匹配原则 Mysql对联合索引有优化么?会自动调整顺序么?哪个版本开始优化? Redis的应用 Redis的持久化的方式和原理 技术选型,一个新技术和一个稳定的旧技术,你会怎么选择,选择的考虑有哪些 说你印象最深的美团点评技术团队的三篇博客 最近在学什么新技术 你是怎么去接触一门新技术的 会看哪些书 怎么选择要看的书 最后 由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦 子 怎么保证项目的进度 数据库的索引原理 非聚簇索引和聚簇索引 索引的使用注意事项 联合索引 从底层解释最左匹配原则 Mysql对联合索引有优化么?会自动调整顺序么?哪个版本开始优化? Redis的应用 Redis的持久化的方式和原理 技术选型,一个新技术和一个稳定的旧技术,你会怎么选择,选择的考虑有哪些 说你印象最深的美团点评技术团队的三篇博客 最近在学什么新技术 你是怎么去接触一门新技术的 会看哪些书 怎么选择要看的书 最后 由于篇幅限制,小编在此截出几张知识讲解的图解,有需要的程序猿(媛)可以点赞后戳这里免费领取全部资料获取哦 [外链图片转存中…(img-SFREePIJ-1624074891834)] [外链图片转存中…(img-5kF3pkiC-1624074891834)] [外链图片转存中…(img-HDVXfOMR-1624074891835)] [外链图片转存中…(img-RyaAC5jy-1624074891836)] [外链图片转存中…(img-iV32C5Ok-1624074891837)] 本篇文章为转载内容。原文链接:https://blog.csdn.net/m0_57285325/article/details/118051767。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-11-13 23:43:59
85
转载
转载文章
...ewholder,去管理 这些id ,然后通过tag 去直接拿到 id。 3, 分页加载,延迟加载 预加载。 这个在我们以前项目,有一个榜单,数据量很大,一次请求过来的数据量很大,这样有两个问题,一个是请求网络 时间可能会很长,另一个展示数据 上面 体验对不是很好,所以 我们做了 第一次加载 20条,然后每次请求 再去 加载10条新数据。 4.就是 对 listview 中一些 类似头像, 图片的 优化。这里 类似 三级缓存,推荐大家看一下 开源 的universal-image-loader 的源码。或者 这篇文章http://www.jb51.net/article/38162.htm,J哥有时间 专门写一篇过于 图片缓存的。 面试官01问: 看你简历上面 做过 社交,通信这块是怎么做的。 J哥回答:我看 咱们公司 也用到了 聊天,咱们公司是 自己做的 还是 用的第三方的类似 环信的。结果被J哥猜中,他说 是集成的环信(但是 有丢包现象,所以打算自己做通信)。 OK,J哥说 ,我们 项目中聊天 是基于xmpp协议的做的,在没有android以前 ,java有个开源的 smack ,android 上 现在有一个asmack ,其实 就是移植到android 中来了, 服务端是基于 openfire的 ,我们就是做的 openfire+asmack 的 聊天,这个原理主要 就是 绑定 ip 拿到 connection 然后 connect ,然后进行通信,我说,这个 跟http请求 其实原理上一样,都是 绑定ip,然后 设置一些property,然后通过类似流进行通信的, asmack,其实底层 就是xml通信的。 面试官01问: touch 事件的传递机制,还特意画了,一个 就是 button LinearLayout 嵌套 。 J哥回答:就是这个, 这也难不倒我。因为J哥觉得 这个问题肯定会问到 所以 早有准备,这里 我就大体说下结论,详细原理 给你传送门。 我回答,这个很简单,只要你继承一下 button 和 linearlayout 复写一下 三个方法 dispatchtouchEvent onInterceptTouchEvent 和onTouchEvent .就能很清楚的明白 传递的过程,我给你总的说下结论的,点击这个button,一般是 外面的父控件 先响应这个down 事件,然后 往子类里面传递,让子类 在往子类的下一级子类去传递,让最终的孩子去决定是不要要消费掉这个点击事件,如果消费掉,那么父类将不会响应,如果子类不消费,那么会退回到次级子类,然后看是否要消费,这样,一句话 就是父传子, 子决定要不要,不要 然后传回去。 这里有很详细 很详细的介绍, 包裹事件的分发。所以我就不罗嗦,http://blog.csdn.net/yanbober/article/details/45887547?ref=myread 面试官01问: 项目中图片的优化。 J哥回答:我给他展示的项目 其中有一款app 是有很多图片 ,但是 很流畅,也没有oom。关于图片 优化,一般我们采用三级缓存,1 。内存加载 2.本地加载 3 网络加载。 首先 我们看 内存中有没有,有直接拿来用,这里 我项目里是这样做的,我先获取一下 分配给我们应用的可用内存是多少,然后 拿1/4 或者 1/8做一个 lrucache. 把我们的bitmap对象添加进去。有些比较常用的图片,我会保存到本地,避免每次重复联网下载。结合 开源的 afinal universalimageloader 以及 13年谷歌官方推荐的volley(号称是 asynchttpclient 和universalimageloader)的结合、 所以 在我的项目中基本没有遇到过图片导致的oom 问题,对于单张的 大图片,我也会利用bitmapFactory,进行计算大小,然后 计算手机分辨率,进行定量的 压缩 处理。 面试官问: GC的回收 J哥回答:我说。GC 回收 应该不只是按照一种方式,应该有多种不同的算法,我看过谷歌 官网介绍的一点,有这样一块区域,他分为 latest(最近) middle(中等)permanent(永久的),这样三块子区域。里面分别存放,刚刚被创建的,以及 时间 靠后的,很久的,对象,不断地新对象 往latest里面添加,当达到相应对象区域的阀值的时候,就会触发GC,GC 进行回收的时候,对于latest 中回收的速度是最快的,而permanent 相对是最久的,而时间 也跟 每块区域中对象的个数有关系, 还有一种算法,是根据最近被引用的时间,或者 被引用的次数 去进行 GC的、、这里随便扯就是了。GC 回收并不是立即执行的。是不定时的。GC回收的时候 会阻塞线程,所以代码中要避免创建不必要的对象,例如for循环中 创建大量对象 就会容易引起GC。 当我们也可以主动 在方法中执行system.gc() 去手动释放一些资源。 面试官01问: 怎么避免 viewpager 预加载 fragment的、 J哥回答:这个问题 我也碰到过,我们都知道,viewpager 它本身会预加载 左右两个 和当前一个对象、而 我们viewpager setOffscreenPageLimit(0) 不生效因为看源码知道,这个方法默认最少也要加载一个。所以 这个fragment 还没有被当前页面显示出来,已经夹在好了,有可能数据不是最新的,我是在 setuservisibilityhint() 这个方法中跟参数 动态去判断 要不要刷新的。 问了一圈,这个哥们大概没什么问的了,然后 就让我等一下,说让他们技术总监过来 。 我就等。。。 然后等了几分钟,进来一小姑娘,坐下,看了我简历,我以为是人事,来跟我谈人生理想。结果,没说几句话,让我讲一下我的项目。我qu,惊呆我了。我问,你也是做android的,我去,是这样的、、把J哥吓到, 然后问了J哥几个问题。 Android 小姑娘问: 看你项目中的listview 中item类型 是统一的,而加入 item 差别挺大的 你怎么复用。 J哥回答:J哥装作很牛的样子说,我暂时想到两种方法,1.给这个对象 加一个type 然后 根据 type 去复用,或者 把这几种类型 一起加载,然后控制显示隐藏。然后 我反问小姑娘,假如 我这里 有一百条数据,这一百条是无序的,包含了 10种 item类型,你有没有什么好方法 去处理这个问题, 小姑娘说,你不是定义了类型吗,我们就是 通过type 去判断的。 Android 小姑娘问: onAttch onDetach还是onAttachedToWindow,onDetachedFromWindow J哥回答:其实 那个小姑娘忘记这两个方法了。我说什么方法,她说onAttachIntent() 和 onDetachIntent(). 反正 J哥是没听说过, 我只见过 onAttach ,但是 这个方法 我也没用过。我就问她,这两个方法是做什么的,小姑娘跟我说 是 把子view绑定到界面上的,那么的话 应该是onAttachedToWindow,onDetachedFromWindow方法了,小姑娘说: 在这个方法 可以计算子 view的高度宽度,在 oncreate 里面不能计算,其实虽然刚开始 在oncreate里面是不能计算,但是还是有方法计算的,(本人觉得面试 问你 API 是 最2的了,忍不住吐槽下,我遇到过,Camera 拍照,问我获取 一个图片,还是 视频的 方法,我去百度 一下,随便就知道,真是不懂 为什么会问方法。随便一个程序员 都会百度。。) 跟小姑娘聊得其他问题 不太记得了,感觉这个女程序员啊。。就问方法 给我的印象不太好,不管方法用没用到,我觉得面试 直接问你方法 好2 好2... 然后技术总监 有进来跟我聊了,后技术总监 有进来跟我聊了、技术总监 年龄30出头吧,到是没有问我什么技术问题, 总监: 问我 做没做过通信这块,能不能做这一块。 J哥回答:,我说做过,通信有几种协议的,我们用的 是xmpp协议的 ,服务器 是 基于apache的 openfire 搭建的,客户端 是用的asmack。还有一些 其他协议的 ,比如我知道有些项目中用的 soap协议的,还有ip 协议的。PS:反正就是扯 我说 通信 客户端这一块 我没问题,但是 服务端 我 从工作以来 一直偏向 android 移动端开发,后台这一块,如果数据量大了,还要考虑并发之类的,我是做不了,让我做个tomcat搭建的demo 我可能可以。 其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。 总监: 问我 什么时候能上班 J哥回答:我说 这个看公司需求啦。 其他也是随便聊了下,然后 就说,让人事来跟我谈理想了。 这里 感觉应该没问题了。差不多能拿下了。 人事1:一进来,就问东问西。问加班看法啊,他们公司技术 一般都八九点走啊。说七点基本没有走的啊、、、 J哥回答:我说,一般遇到项目加功能 ,版本升级,等等 这些加班都没什么,只要不是一直在加班。。。。这里每个人自己看法就好了、、 反正人事 是一直跟我强调这个,她不停强调 我就暗暗下决心,薪资 我是不会要低了。 人事1:看你还年轻啊,还能拼一拼啊、、、、 J哥回答:我说现在 这几年对我人生规划也算比较重要的时期,也是过一年少一年了,其实她的意思 还是侧面强调加班。。。。日了UZI了。 中间一堆废话,然后我问了她 公司一般上下班时间啊。。之类的有没有技术交流啊,之类的。。。 最后到关键问题上啦,最关心的,薪资问题。 人事1:期望薪资 J哥回答:我说16K左右吧。她问 你以前公司多少 握手 15K。她说她们公司 是 14薪。反正 我还是说16K。她说 那好,你等下,然后就出去了。 不知道 跟什么人 讨论了许久,然后又来一个 可能是人事吧。又进来,问了一遍,也问了薪资。。哥还是说16K 。 。。估计是她们公司想要我,但是又觉得有点超出她们薪资期望吧,当场被没有给什么offer。然后就有点婉拒的说,两天给我答复,心里很气愤,饿着肚子 面试到三点,竟然婉拒、、、 反正我是很生气,我说,好,然后我就走。结果,没过一个小时,人事又打电话来,非要约我 见一下她们CEO。这是什么鬼,难道她们CEO要给我煲汤 了?我说可以,然后时间定在后天了,,反正心灵鸡汤对我是没用了、 OK ,这家面试 先写到这里,下面下午还有一家,等下在写。准备睡觉。今天面试回来,累的就睡着了,晚上十点多才醒过来,想了想还是 把今天面试的过程总结一下。 ------------------------------待续------------------------- 第二弹http://blog.csdn.net/u011733020/article/details/46058273 本篇文章为转载内容。原文链接:https://blog.csdn.net/haluoluo211/article/details/51010955。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-19 17:42:52
336
转载
转载文章
这篇文章详述了在Linux系统中进行硬件设备(声卡、显卡和网卡)设置的方法,包括如何通过sndconfig配置声卡并检测其成功性;利用Xconfigurator或xfree86等工具设置显卡参数以适应不同类型的显卡与显示器。此外,文章还介绍了播放CD音乐及VCD影碟的具体步骤,并提及了解决双系统启动默认选择、中文乱码、缺失库文件等问题的技巧。针对Linux下图形界面操作,用户可学习如何让系统开机默认进入X-Window图形界面以及同时启动多个X会话。关键词涵盖:Linux、设置、声卡、显卡、网卡、播放CD、VCD影碟、拨号上网、X-Window及游戏。
2023-10-27 09:27:49
255
转载
转载文章
...束流量立即回收,提高管理效率。 这能帮助公司降低分析成本,加快算法迭代效应,使整个系统的算法优化工作能够快速往前推进。 这是头条A/B Test实验系统的基本原理。首先我们会做在离线状态下做好用户分桶,然后线上分配实验流量,将桶里用户打上标签,分给实验组。 举个例子,开一个10%流量的实验,两个实验组各5%,一个5%是基线,策略和线上大盘一样,另外一个是新的策略。 实验过程中用户动作会被搜集,基本上是准实时,每小时都可以看到。但因为小时数据有波动,通常是以天为时间节点来看。动作搜集后会有日志处理、分布式统计、写入数据库,非常便捷。 在这个系统下工程师只需要设置流量需求、实验时间、定义特殊过滤条件,自定义实验组ID。系统可以自动生成:实验数据对比、实验数据置信度、实验结论总结以及实验优化建议。 当然,只有实验平台是远远不够的。线上实验平台只能通过数据指标变化推测用户体验的变化,但数据指标和用户体验存在差异,很多指标不能完全量化。 很多改进仍然要通过人工分析,重大改进需要人工评估二次确认。 五、内容安全 最后要介绍今日头条在内容安全上的一些举措。头条现在已经是国内最大的内容创作与分发凭条,必须越来越重视社会责任和行业领导者的责任。如果1%的推荐内容出现问题,就会产生较大的影响。 现在,今日头条的内容主要来源于两部分,一是具有成熟内容生产能力的PGC平台 一是UGC用户内容,如问答、用户评论、微头条。这两部分内容需要通过统一的审核机制。如果是数量相对少的PGC内容,会直接进行风险审核,没有问题会大范围推荐。 UGC内容需要经过一个风险模型的过滤,有问题的会进入二次风险审核。审核通过后,内容会被真正进行推荐。这时如果收到一定量以上的评论或者举报负向反馈,还会再回到复审环节,有问题直接下架。 整个机制相对而言比较健全,作为行业领先者,在内容安全上,今日头条一直用最高的标准要求自己。 分享内容识别技术主要鉴黄模型,谩骂模型以及低俗模型。今日头条的低俗模型通过深度学习算法训练,样本库非常大,图片、文本同时分析。 这部分模型更注重召回率,准确率甚至可以牺牲一些。谩骂模型的样本库同样超过百万,召回率高达95%+,准确率80%+。如果用户经常出言不讳或者不当的评论,我们有一些惩罚机制。 泛低质识别涉及的情况非常多,像假新闻、黑稿、题文不符、标题党、内容质量低等等,这部分内容由机器理解是非常难的,需要大量反馈信息,包括其他样本信息比对。 目前低质模型的准确率和召回率都不是特别高,还需要结合人工复审,将阈值提高。目前最终的召回已达到95%,这部分其实还有非常多的工作可以做。别平台。 如果需要机器学习视频,可以在公众号后台聊天框回复【机器学习】,可以免费获取编程视频 。 你可能还喜欢 数学在机器学习中到底有多重要? AI 新手学习路线,附上最详细的资源整理! 提升机器学习数学基础,推荐7本书 酷爆了!围观2020年十大科技趋势 机器学习该如何入门,听听过来人的经验! 长按加入T圈,接触人工智能 觉得内容还不错的话,给我点个“在看”呗 本篇文章为转载内容。原文链接:https://blog.csdn.net/itcodexy/article/details/109574173。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2024-01-13 09:21:23
322
转载
转载文章
...在每次提交到源码版本管理系统时触发 计划任务:预配置好的计划 手动:无论是通过CI服务器的管理界面还是脚本,用户可以手工执行CI工作流 代码审核 可在持续集成服务器里使用代码分析工具(例如Sonar)来执行自动代码审查 自动代码审查通过后,可发起一个人工代码审查,揪出那些自动审查无法找出的问题,即验证业务需求,架构问题,代码是否可读,以及是否易于扩展。 可灵活配置代码审核策略,例如:如果某些人没有审查代码便阻止对主干分支的任何提交。 最常用的工具是Gerrit 持续交付 简述 持续交付简称CD或CDE,是一种能够使得软件在较短的循环中可靠的发布的软件工程方法 与持续集成相比,持续交付的重点在于 交付,其核心对象不在于代码,而在于可交付的产物。 由于持续集成仅仅针对于新旧代码的集成过程执行来了一定的测试,其变动到持续交付后还需要一些额外的流程 持续交付可以看作为是持续集成的下一步,它强调的是,不敢怎么更新,软件是随时随快可以交付的 有图可看出,持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实的运行环境的[类生产环境]中 目的 持续交付永爱确保让代码能够快速、安全的部署到产品环境中,它通过将每一次改动都会提交到一个模拟产品环境中,使用严格的自动化测试,确保业务应用和服务能符合预期 好处 持续交付和持续集成的好处非常相似: 快速发布。能够应对业务需求,并更快地实现软件价值 编码→测试→上线→交付的频繁迭代周期缩短,同时获得迅速反馈 高质量的软件发布标准。整个交付过程标准化、可重复、可靠 整个交付过程进度可视化,方便团队人员了解项目完成度 更先进的团队协作方式。从需求分析、产品的用户体验到交互、设计、开发、测试、运维等角色密切协作,相比于传统的瀑布式软件团队,更少浪费 持续部署 简述 持续部署 意味着:通过自动化部署的手段将软件功能频繁的进行交付 持续部署是持续交付的下一步,指的是代码通过审批以后,自动化部署到生产环境。 持续部署是持续交付的最高阶段,这意味着,所有通过了一系列的自动化测试的改动都将自动部署到生产环境。它也可以被称为“Continuous Release” 持续化部署的目标是:代码在任何时候都是可部署的,可以进入生产阶段。 持续部署的前提是能自动化完成测试、构建、部署等步骤 注:持续交付不等于持续集成 与持续交付以及持续集成相比,持续部署强调了通过 automated deployment 的手段,对新的软件功能进行集成 目标 持续部署的目标是:代码在任何时刻都是可部署的,可以进入生产阶段 有很多的业务场景里,一种业务需要等待另外的功能特征出现才能上线,这是的持续部署成为不可能。虽然使用功能切换能解决很多这样的情况,但并不是没每次都会这样。所以,持续部署是否适合你的公司是基于你们的业务需求——而不是技术限制 优点 持续部署主要的好处是:可以相对独立地部署新的功能,并能快速地收集真实用户的反馈 敏捷开发 简述 敏捷开发就是一种以人为核心、迭代循环渐进的开发方式。 在敏捷开发中,软件仙姑的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。 简单的说就是把一个大的项目分为多个相互联系,但也可以独立运行的小项目,并分别完成,在此过程中软件一直处于可使用状态 注意事项 敏捷开的就是一种面临迅速变化的需求快速开发的能力,要注意一下几点: 敏捷开发不仅仅是一个项目快速完成,而是对整个产品领域需求的高效管理 敏捷开发不仅仅是简单的快,而是短周期的不断改进、提高和调整 敏捷开发不仅仅是一个版本只做几个功能,而是突出重点、果断放弃当前的非重要点 敏捷开发不仅仅是随时增加需求,而是每个迭代周期对需求的重新审核和排序 如何进行敏捷开发 1、组织建设 也就是团队建设,建立以产品经理为主导,包含产品、设计、前后台开发和测试的team,快速进行产品迭代开发;扁平化的团队管理,大家都有共同目标,更有成就感; 2、敏捷制度 要找准适合自身的敏捷开发方式,主要是制定一个完善的效率高的设计、开发、测试、上线流程,制定固定的迭代周期,让用户更有期待; 3、需求收集 这个任何方式下都需要有,需求一定要有交互稿,评审通过后,一定要确定功能需求列表、责任人、工作量、责任人等; 4、工具建设 是指能够快速完成某项事情的辅助工具,比如开发环境的一键安装,各种底层的日志、监控等平台,发布、打包工具等; 5、系统架构 略为超前架构设计:支持良好的扩容性和可维护性;组件化基础功能模块:代码耦合度低,模块间的依赖性小;插件化业务模块:降低营销活动与业务耦合度,自升级、自维护;客户端预埋逻辑;技术预研等等; 6、数据运营与灰度发布 点击率分析、用户路径分析、渠道选择、渠道升级控制等等 原则、特点和优势 敏捷开发技术的12个原则: 1.我们最优先要做的是通过尽早的、持续的交付有价值的软件来使客户满意。 2.即使到了开发的后期,也欢迎改变需求。 3.经常性地交付可以工作的软件,交付的间隔可以从几周到几个月,交付的时间间隔越短越好。 4.在整个项目开发期间,业务人员和开发人员必须天天都在一起工作。 5.围绕被激励起来的个人来构建项目。 6.在团队内部,最具有效果并且富有效率的传递信息的方法,就是面对面的交谈。 7.工作的软件是首要的进度度量标准。 8.敏捷过程提倡可持续的开发速度。 9.不断地关注优秀的技能和好的设计会增强敏捷能力。 10.简单使未完成的工作最大化。 11.最好的构架、需求和设计出自于自组织的团队。 12.每隔一定时间,团队会在如何才能更有效地工作方面进行反省,然后相应地对自己的行为进行调整。 特点: 个体和交互胜过过程和工具 可以工作的软件胜过面面俱到的文档 客户合作胜过合同谈判 响应变化胜过遵循计划 优势总结: 敏捷开发确实是项目进入实质开发迭代阶段,用户很快可以看到一个基线架构班的产品。敏捷注重市场快速反应能力,也即具体应对能力,客户前期满意度高 适用范围: 项目团队的人不能太多 项目经常发生变更 高风险的项目实施 开发人员可以参与决策 劣势总结: 敏捷开发注重人员的沟通 忽略文档的重要性 若项目人员流动太大,维护的时候很难 项目存在新手的比较多的时候,老员工会比较累 需要项目中存在经验较强的人,要不然大项目中容易遇到瓶颈问题 Open-falcon 简述 open-falcon是小米的监控系统,是一款企业级、高可用、可扩展的开源监控解决方案 公司用open-falcon来监控调度系统各种信息,便于监控各个节点的调度信息。在服务器安装了falcon-agent自动采集各项指标,主动上报 特点 强大灵活的数据采集 (自动发现,支持falcon-agent、snmp、支持用户主动push、用户自定义插件支持、opentsdb data model like(timestamp、endpoint、metric、key-value tags) ) 水平扩展能力 (支持每个周期上亿次的数据采集、告警判定、历史数据存储和查询 ) 高效率的告警策略管理 (高效的portal、支持策略模板、模板继承和覆盖、多种告警方式、支持callback调用 ) 人性化的告警设置 (最大告警次数、告警级别、告警恢复通知、告警暂停、不同时段不同阈值、支持维护周期 ) 高效率的graph组件 (单机支撑200万metric的上报、归档、存储(周期为1分钟) ) 高效的历史数据query组件 (采用rrdtool的数据归档策略,秒级返回上百个metric一年的历史数据 ) dashboard(面向用户的查询界面,可以看到push到graph中的所有数据,并查看数据发展趋势 ) (对维度的数据展示,用户自定义Screen) 高可用 (整个系统无核心单点,易运维,易部署,可水平扩展) 开发语言 (整个系统的后端,全部golang编写,portal和dashboard使用python编写。 ) 监控范围 Open-Falcon支持系统基础监控,第三方服务监控,JVM监控,业务应用监控 基础监控指的是Linux系统的指标监控,包括CPU、load、内存、磁盘、IO、网络等, 这些指标由Openfalcon的agent节点直接支持,无需插件 第三方服务监控指的是一些常见的服务监控,包括Mysql、Redis、Nginx等 OpenFalcon官网提供了很多第三方服务的监控插件,也可以自己实现插件,定义采集指标。而采集到的指标,也是通过插件先发送给agent,再由agent发送到OpenFalcon。 JVM监控主要通过插件完成,插件通过JVM开放的JMX通信端口,获取到JVM参数指标,并推送到agent节点,再由agent发送到OpenFalcon。 业务应用监控就是监控企业自主开发的应用服务 主要通过插件完成,插件通过JVM开放的JMX通信端口,获取到JVM参数指标,并推送到agent节点,再由agent发送到OpenFalcon。 数据流向 常见的OpenFalcon包含transfer、hbs、agent、judge、graph、API几个进程 以下是各个节点的数据流向图,主数据流向是agent -> transfer -> judge/graph: SNMP 简述 SNMP:简单网络管理协议,是TCP/IP协议簇 的一个应用层协议,由于SNMP的简单性,在Internet时代得到了蓬勃的发展 ,1992年发布了SNMPv2版本,以增强SNMPv1的安全性和功能。现在,已经有了SNMPv3版本(它对网络管理最大的贡献在于其安全性。增加了对认证和密文传输的支持 )。 一套完整的SNMP系统主要包括:管理信息库(MIB)、管理信息结构(SMI)和 SNMP报文协议 为什么要用SNMP 作为运维人员,我们很大一部分的工作就是为了保证我们的网络能够正常、稳定的运行。因此监控,控制,管理各种网络设备成了我们日常的工作 优点和好处 优点: 简单易懂,部署的开销成本也小 ,正因为它足够简单,所以被广泛的接受,事实上它已经成为了主要的网络管理标准。在一个网络设备上实现SNMP的管理比绝大部分其他管理方式都简单直接。 好处: 标准化的协议:SNMP是TCP/IP网络的标准网络管理协议。 广泛认可:所有主流供应商都支持SNMP。 可移植性:SNMP独立于操作系统和编程语言。 轻量级:SNMP增强对设备的管理能力的同时不会对设备的操作方式或性能产生冲击。 可扩展性:在所有SNMP管理的设备上都会支持相同的一套核心操作集。 广泛部署:SNMP是最流行的管理协议,最为受设备供应商关注,被广泛部署在各种各样的设备上。 MIB、SMI和SNMP报文 MIB 管理信息库MIB:任何一个被管理的资源都表示成一个对象,称为被管理的对象。 MIB是被管理对象的集合。 它定义了被管理对象的一系列属性:对象的名称、对象的访问权限和对象的数据类型等。 每个SNMP设备(Agent)都有自己的MIB。 MIB也可以看作是NMS(网管系统)和Agent之间的沟通桥梁。 MIB文件中的变量使用的名字取自ISO和ITU管理的对象表示符命名空间,他是一个分级数的结构 SMI SMI定义了SNNMP框架多用信息的组织、组成和标识,它还未描述MIB对象和表述协议怎么交换信息奠定了基础 SMI定义的数据类型: 简单类型(simple): Integer:整型是-2,147,483,648~2,147,483,647的有符号整数 octet string: 字符串是0~65535个字节的有序序列 OBJECT IDENTIFIER: 来自按照ASN.1规则分配的对象标识符集 简单结构类型(simple-constructed ): SEQUENCE 用于列表。这一数据类型与大多数程序设计语言中的“structure”类似。一个SEQUENCE包括0个或更多元素,每一个元素又是另一个ASN.1数据类型 SEQUENCE OF type 用于表格。这一数据类型与大多数程序设计语言中的“array”类似。一个表格包括0个或更多元素,每一个元素又是另一个ASN.1数据类型。 应用类型(application-wide): IpAddress: 以网络序表示的IP地址。因为它是一个32位的值,所以定义为4个字节; counter:计数器是一个非负的整数,它递增至最大值,而后回零。在SNMPv1中定义的计数器是32位的,即最大值为4,294,967,295; Gauge :也是一个非负整数,它可以递增或递减,但达到最大值时保持在最大值,最大值为232-1; time ticks:是一个时间单位,表示以0.01秒为单位计算的时间; SNMP报文 SNMP规定了5种协议数据单元PDU(也就是SNMP报文),用来在管理进程和代理之间的交换。 get-request操作:从代理进程处提取一个或多个参数值。 get-next-request操作:从代理进程处提取紧跟当前参数值的下一个参数值。 set-request操作:设置代理进程的一个或多个参数值。 get-response操作:返回的一个或多个参数值。这个操作是由代理进程发出的,它是前面三种操作的响应操作。 trap操作:代理进程主动发出的报文,通知管理进程有某些事情发生。 操作命令 SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令。它们是:Get、Set 和 Trap。 Get:管理站读取代理者处对象的值 Set:管理站设置代理者处对象的值 Trap: 代理者主动向管理站通报重要事件 SLA 简述 SLA(服务等级协议):是关于网络服务供应商和客户之间的一份合同,其中定义了服务类型、服务质量和客户付款等术语 一个完整的SLA同时也是一个合法的文档,包括所涉及的当事人、协定条款(包含应用程序和支持的服务)、违约的处罚、费用和仲裁机构、政策、修改条款、报告形式和双方的义务等。同样服务提供商可以对用户在工作负荷和资源使用方面进行规定。 KPI 简述 KPI(关键绩效指标):是通过对组织内部流程的输入端、输出端的关键参数进行设置、取样、计算、分析,衡量流程绩效的一种目标式量化管理指标,是把企业的战略目标分解为可操作的工作目标的工具,是企业绩效管理的基础。 KPI可以是部门主管明确部门的主要责任,并以此为基础,明确部门人员的业绩衡量指标,建立明确的切实可行的KPI体系,是做好绩效管理的关键。 KPI(关键绩效指标)是用于衡量工作人员工作绩效表现的量化指标,是绩效计划的重要组成部分 转载于:https://www.cnblogs.com/woshinideyugegea/p/11242034.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/anqiongsha8211/article/details/101592137。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-19 16:00:05
45
转载
转载文章
...特权的用户,以便允许管理员登录,即使已经达到连接限制。 max_connections=151 The number of open tables for all threads. Increasing this value increases the number of file descriptors that mysqld requires. Therefore you have to make sure to set the amount of open files allowed to at least 4096 in the variable "open-files-limit" in 为所有线程打开的表的数量。增加这个值会增加mysqld需要的文件描述符的数量。因此,您必须确保在[mysqld_safe]节中的变量“open-files-limit”中将允许打开的文件数量至少设置为4096 section [mysqld_safe] table_open_cache=2000 Maximum size for internal (in-memory) temporary tables. If a table grows larger than this value, it is automatically converted to disk based table This limitation is for a single table. There can be many of them. 内部(内存)临时表的最大大小。如果一个表比这个值大,那么它将自动转换为基于磁盘的表。可以有很多。 tmp_table_size=94M How many threads we should keep in a cache for reuse. When a client disconnects, the client's threads are put in the cache if there aren't more than thread_cache_size threads from before. This greatly reduces the amount of thread creations needed if you have a lot of new connections. (Normally this doesn't give a notable performance improvement if you have a good thread implementation.) 我们应该在缓存中保留多少线程以供重用。当客户机断开连接时,如果之前的线程数不超过thread_cache_size,则将客户机的线程放入缓存。如果您有很多新连接,这将大大减少所需的线程创建量(通常,如果您有一个良好的线程实现,这不会带来显著的性能改进)。 thread_cache_size=10 MyISAM Specific options The maximum size of the temporary file MySQL is allowed to use while recreating the index (during REPAIR, ALTER TABLE or LOAD DATA INFILE. If the file-size would be bigger than this, the index will be created through the key cache (which is slower). MySQL允许在重新创建索引时(在修复、修改表或加载数据时)使用临时文件的最大大小。如果文件大小大于这个值,那么索引将通过键缓存创建(这比较慢)。 myisam_max_sort_file_size=100G If the temporary file used for fast index creation would be bigger than using the key cache by the amount specified here, then prefer the key cache method. This is mainly used to force long character keys in large tables to use the slower key cache method to create the index. myisam_sort_buffer_size=179M Size of the Key Buffer, used to cache index blocks for MyISAM tables. Do not set it larger than 30% of your available memory, as some memory is also required by the OS to cache rows. Even if you're not using MyISAM tables, you should still set it to 8-64M as it will also be used for internal temporary disk tables. 如果用于快速创建索引的临时文件比这里指定的使用键缓存的文件大,则首选键缓存方法。这主要用于强制大型表中的长字符键使用较慢的键缓存方法来创建索引。 key_buffer_size=8M Size of the buffer used for doing full table scans of MyISAM tables. Allocated per thread, if a full scan is needed. 用于对MyISAM表执行全表扫描的缓冲区的大小。如果需要完整的扫描,则为每个线程分配。 read_buffer_size=256K read_rnd_buffer_size=512K INNODB Specific options INNODB特定选项 innodb_data_home_dir= Use this option if you have a MySQL server with InnoDB support enabled but you do not plan to use it. This will save memory and disk space and speed up some things. 如果您启用了一个支持InnoDB的MySQL服务器,但是您不打算使用它,那么可以使用这个选项。这将节省内存和磁盘空间,并加快一些事情。skip-innodb skip-innodb If set to 1, InnoDB will flush (fsync) the transaction logs to the disk at each commit, which offers full ACID behavior. If you are willing to compromise this safety, and you are running small transactions, you may set this to 0 or 2 to reduce disk I/O to the logs. Value 0 means that the log is only written to the log file and the log file flushed to disk approximately once per second. Value 2 means the log is written to the log file at each commit, but the log file is only flushed to disk approximately once per second. 如果设置为1,InnoDB将在每次提交时将事务日志刷新(fsync)到磁盘,这将提供完整的ACID行为。如果您愿意牺牲这种安全性,并且正在运行小型事务,您可以将其设置为0或2,以将磁盘I/O减少到日志。值0表示日志仅写入日志文件,日志文件大约每秒刷新一次磁盘。值2表示日志在每次提交时写入日志文件,但是日志文件大约每秒只刷新一次磁盘。 innodb_flush_log_at_trx_commit=1 The size of the buffer InnoDB uses for buffering log data. As soon as it is full, InnoDB will have to flush it to disk. As it is flushed once per second anyway, it does not make sense to have it very large (even with long transactions).InnoDB用于缓冲日志数据的缓冲区大小。一旦它满了,InnoDB就必须将它刷新到磁盘。由于它无论如何每秒刷新一次,所以将它设置为非常大的值是没有意义的(即使是长事务)。 innodb_log_buffer_size=5M InnoDB, unlike MyISAM, uses a buffer pool to cache both indexes and row data. The bigger you set this the less disk I/O is needed to access data in tables. On a dedicated database server you may set this parameter up to 80% of the machine physical memory size. Do not set it too large, though, because competition of the physical memory may cause paging in the operating system. Note that on 32bit systems you might be limited to 2-3.5G of user level memory per process, so do not set it too high. 与MyISAM不同,InnoDB使用缓冲池来缓存索引和行数据。设置的值越大,访问表中的数据所需的磁盘I/O就越少。在专用数据库服务器上,可以将该参数设置为机器物理内存大小的80%。但是,不要将它设置得太大,因为物理内存的竞争可能会导致操作系统中的分页。注意,在32位系统上,每个进程的用户级内存可能被限制在2-3.5G,所以不要设置得太高。 innodb_buffer_pool_size=20M Size of each log file in a log group. You should set the combined size of log files to about 25%-100% of your buffer pool size to avoid unneeded buffer pool flush activity on log file overwrite. However, note that a larger logfile size will increase the time needed for the recovery process. 日志组中每个日志文件的大小。您应该将日志文件的合并大小设置为缓冲池大小的25%-100%,以避免在覆盖日志文件时出现不必要的缓冲池刷新活动。但是,请注意,较大的日志文件大小将增加恢复过程所需的时间。 innodb_log_file_size=48M Number of threads allowed inside the InnoDB kernel. The optimal value depends highly on the application, hardware as well as the OS scheduler properties. A too high value may lead to thread thrashing. InnoDB内核中允许的线程数。最优值在很大程度上取决于应用程序、硬件以及OS调度程序属性。过高的值可能导致线程抖动。 innodb_thread_concurrency=9 The increment size (in MB) for extending the size of an auto-extend InnoDB system tablespace file when it becomes full. 增量大小(以MB为单位),用于在表空间满时扩展自动扩展的InnoDB系统表空间文件的大小。 innodb_autoextend_increment=128 The number of regions that the InnoDB buffer pool is divided into. For systems with buffer pools in the multi-gigabyte range, dividing the buffer pool into separate instances can improve concurrency, by reducing contention as different threads read and write to cached pages. InnoDB缓冲池划分的区域数。对于具有多gb缓冲池的系统,将缓冲池划分为单独的实例可以提高并发性,因为不同的线程对缓存页面的读写会减少争用。 innodb_buffer_pool_instances=8 Determines the number of threads that can enter InnoDB concurrently. 确定可以同时进入InnoDB的线程数 innodb_concurrency_tickets=5000 Specifies how long in milliseconds (ms) a block inserted into the old sublist must stay there after its first access before it can be moved to the new sublist. 指定插入到旧子列表中的块必须在第一次访问之后停留多长时间(毫秒),然后才能移动到新子列表。 innodb_old_blocks_time=1000 It specifies the maximum number of .ibd files that MySQL can keep open at one time. The minimum value is 10. 它指定MySQL一次可以打开的.ibd文件的最大数量。最小值是10。 innodb_open_files=300 When this variable is enabled, InnoDB updates statistics during metadata statements. 当启用此变量时,InnoDB会在元数据语句期间更新统计信息。 innodb_stats_on_metadata=0 When innodb_file_per_table is enabled (the default in 5.6.6 and higher), InnoDB stores the data and indexes for each newly created table in a separate .ibd file, rather than in the system tablespace. 当启用innodb_file_per_table(5.6.6或更高版本的默认值)时,InnoDB将每个新创建的表的数据和索引存储在单独的.ibd文件中,而不是系统表空间中。 innodb_file_per_table=1 Use the following list of values: 0 for crc32, 1 for strict_crc32, 2 for innodb, 3 for strict_innodb, 4 for none, 5 for strict_none. 使用以下值列表:0表示crc32, 1表示strict_crc32, 2表示innodb, 3表示strict_innodb, 4表示none, 5表示strict_none。 innodb_checksum_algorithm=0 The number of outstanding connection requests MySQL can have. This option is useful when the main MySQL thread gets many connection requests in a very short time. It then takes some time (although very little) for the main thread to check the connection and start a new thread. The back_log value indicates how many requests can be stacked during this short time before MySQL momentarily stops answering new requests. You need to increase this only if you expect a large number of connections in a short period of time. MySQL可以有多少未完成连接请求。当MySQL主线程在很短的时间内收到许多连接请求时,这个选项非常有用。然后,主线程需要一些时间(尽管很少)来检查连接并启动一个新线程。back_log值表示在MySQL暂时停止响应新请求之前的短时间内可以堆多少个请求。只有当您预期在短时间内会有大量连接时,才需要增加这个值。 back_log=80 If this is set to a nonzero value, all tables are closed every flush_time seconds to free up resources and synchronize unflushed data to disk. This option is best used only on systems with minimal resources. 如果将该值设置为非零值,则每隔flush_time秒关闭所有表,以释放资源并将未刷新的数据同步到磁盘。这个选项最好只在资源最少的系统上使用。 flush_time=0 The minimum size of the buffer that is used for plain index scans, range index scans, and joins that do not use 用于普通索引扫描、范围索引扫描和不使用索引执行全表扫描的连接的缓冲区的最小大小。 indexes and thus perform full table scans. join_buffer_size=200M The maximum size of one packet or any generated or intermediate string, or any parameter sent by the mysql_stmt_send_long_data() C API function. 由mysql_stmt_send_long_data() C API函数发送的一个包或任何生成的或中间字符串或任何参数的最大大小 max_allowed_packet=500M If more than this many successive connection requests from a host are interrupted without a successful connection, the server blocks that host from performing further connections. 如果在没有成功连接的情况下中断了来自主机的多个连续连接请求,则服务器将阻止主机执行进一步的连接。 max_connect_errors=100 Changes the number of file descriptors available to mysqld. You should try increasing the value of this option if mysqld gives you the error "Too many open files". 更改mysqld可用的文件描述符的数量。如果mysqld给您的错误是“打开的文件太多”,您应该尝试增加这个选项的值。 open_files_limit=4161 If you see many sort_merge_passes per second in SHOW GLOBAL STATUS output, you can consider increasing the sort_buffer_size value to speed up ORDER BY or GROUP BY operations that cannot be improved with query optimization or improved indexing. 如果在SHOW GLOBAL STATUS输出中每秒看到许多sort_merge_passes,可以考虑增加sort_buffer_size值,以加快ORDER BY或GROUP BY操作的速度,这些操作无法通过查询优化或改进索引来改进。 sort_buffer_size=1M The number of table definitions (from .frm files) that can be stored in the definition cache. If you use a large number of tables, you can create a large table definition cache to speed up opening of tables. The table definition cache takes less space and does not use file descriptors, unlike the normal table cache. The minimum and default values are both 400. 可以存储在定义缓存中的表定义的数量(来自.frm文件)。如果使用大量表,可以创建一个大型表定义缓存来加速表的打开。与普通的表缓存不同,表定义缓存占用更少的空间,并且不使用文件描述符。最小值和默认值都是400。 table_definition_cache=1400 Specify the maximum size of a row-based binary log event, in bytes. Rows are grouped into events smaller than this size if possible. The value should be a multiple of 256. 指定基于行的二进制日志事件的最大大小,单位为字节。如果可能,将行分组为小于此大小的事件。这个值应该是256的倍数。 binlog_row_event_max_size=8K If the value of this variable is greater than 0, a replication slave synchronizes its master.info file to disk. (using fdatasync()) after every sync_master_info events. 如果该变量的值大于0,则复制奴隶将其主.info文件同步到磁盘。(在每个sync_master_info事件之后使用fdatasync())。 sync_master_info=10000 If the value of this variable is greater than 0, the MySQL server synchronizes its relay log to disk. (using fdatasync()) after every sync_relay_log writes to the relay log. 如果这个变量的值大于0,MySQL服务器将其中继日志同步到磁盘。(在每个sync_relay_log写入到中继日志之后使用fdatasync())。 sync_relay_log=10000 If the value of this variable is greater than 0, a replication slave synchronizes its relay-log.info file to disk. (using fdatasync()) after every sync_relay_log_info transactions. 如果该变量的值大于0,则复制奴隶将其中继日志.info文件同步到磁盘。(在每个sync_relay_log_info事务之后使用fdatasync())。 sync_relay_log_info=10000 Load mysql plugins at start."plugin_x ; plugin_y". 开始时加载mysql插件。“plugin_x;plugin_y” plugin_load The TCP/IP Port the MySQL Server X Protocol will listen on. MySQL服务器X协议将监听TCP/IP端口。 loose_mysqlx_port=33060 本篇文章为转载内容。原文链接:https://blog.csdn.net/mywpython/article/details/89499852。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-10-08 09:56:02
129
转载
转载文章
...bernetes容器管理平台与Serverless容器之间互通的问题,需要基于具体厂家提供的能力来评估。 基于容器技术实现计算调度平台 计算(大数据/AI训练等)场景的特征是短时间内需要大量算力,算完即释放。容器的环境一致性以及调度便利性适合这种场景。 技术选型 容器技术是属于基础设施范围,但是与传统虚拟化技术(Xen/KVM)比较,容器技术是应用虚拟化,不是纯粹的资源虚拟化,与传统虚拟化存在差异。在容器技术选型时候,需要结合当前团队在应用管理与资源管理的现状,对照容器技术与虚拟化技术的差异,选择最合适的容器技术栈。 什么是容器技术 (1)容器是一种轻量化的应用虚拟化技术。 在讨论具体的容器技术栈的时候,先介绍目前几种常用的应用虚拟化技术,当前有3种主流的应用虚拟化技术: LXC,MicroVM,UniKernel(LibOS)。 LXC: Linux Container,通过 Linux的 namespace/cgroups/chroot 等技术隔离进程资源,目前应用最广的docker就是基于LXC实现应用虚拟化的。 MicroVM: MicroVM 介于 传统的VM 与 LXC之间,隔离性比LXC好,但是比传统的VM要轻量,轻量体现在体积小(几M到几十M)、启动快(小于1s)。 AWS Firecracker 就是一种MicroVM的实现,用于AWS的Serverless计算领域,Serverless要求启动快,租户之间隔离性好。 UniKernel: 是一种专用的(特定编程语言技术栈专用)、单地址空间、使用 library OS 构建出来的镜像。UniKernel要解决的问题是减少应用软件的技术栈层次,现代软件层次太多导致越来越臃肿:硬件+HostOS+虚拟化模拟+GuestOS+APP。UniKernel目标是:硬件+HostOS+虚拟化模拟+APP-with-libos。 三种技术对比表: 开销 体积 启动速度 隔离/安全 生态 LXC 低(几乎为0) 小 快(等同进程启动) 差(内核共享) 好 MicroVM 高 大 慢(小于1s) 好 中(Kata项目) UniKernel 中 中 中 好 差 根据上述对比来看,LXC是应用虚拟化首选的技术,如果LXC无法满足隔离性要,则可以考虑MicroVM这种技术。当前社区已经在着手融合LXC与MicroVM这两种技术,从应用打包/发布调度/运行层面统一规范,Kubernetes集成Kata支持混合应用调度特性可以了解一下。 UniKernel 在应用生态方面相对比较落后,目前在追赶中,目前通过 linuxkit 工具可以在UniKernel应用镜像中使用docker镜像。这种方式笔者还未验证过,另外docker镜像运行起来之后,如何监控目前还未知。 从上述三种应用虚拟化技术对比,可以得出结论: (2)容器技术与传统虚拟化技术不断融合中。 再从规范视角来看容器技术,可以将容器技术定义为: (3)容器=OCI+CRI+辅助工具。 OCI规范包含两部分,镜像规范与运行时规范。简要的说,要实现一个OCI的规范,需要能够下载镜像并解压镜像到文件系统上组成成一个文件目录结构,运行时工具能够理解这个目录结构并基于此目录结构管理(创建/启动/停止/删除)进程。 容器(container)的技术构成就是实现OCI规范的技术集合。 对于不同的操作系统(Linux/Windows),OCI规范的实现技术不同,当前docker的实现,支持Windows与Linux与MacOS操作系统。当前使用最广的是Linux系统,OCI的实现,在Linux上组成容器的主要技术: chroot: 通过分层文件系统堆叠出容器进程的rootfs,然后通过chroot设置容器进程的根文件系统为堆叠出的rootfs。 cgroups: 通过cgroups技术隔离容器进程的cpu/内存资源。 namesapce: 通过pid, uts, mount, network, user namesapce 分别隔离容器进程的进程ID,时间,文件系统挂载,网络,用户资源。 网络虚拟化: 容器进程被放置到独立的网络命名空间,通过Linux网络虚拟化veth, macvlan, bridge等技术连接主机网络与容器虚拟网络。 存储驱动: 本地文件系统,使用容器镜像分层文件堆叠的各种实现驱动,当前推荐的是overlay2。 广义的容器还包含容器编排,即当下很火热的Kubernetes。Kubernetes为了把控容器调度的生态,发布了CRI规范,通过CRI规范解耦Kubelet与容器,只要实现了CRI接口,都可以与Kubelet交互,从而被Kubernetes调度。OCI规范的容器实现与CRI标准接口对接的实现是CRI-O。 辅助工具用户构建镜像,验证镜像签名,管理存储卷等。 容器定义 容器是一种轻量化的应用虚拟化技术。 容器=OCI+CRI+辅助工具。 容器技术与传统虚拟化技术不断融合中。 什么是容器编排与调度 选择了应用虚拟化技术之后,还需要应用调度编排,当前Kubernetes是容器领域内编排的事实标准,不管使用何种应用虚拟化技术,都已经纳入到了Kubernetes治理框架中。 Kubernetes 通过 CRI 接口规范,将应用编排与应用虚拟化实现解耦:不管使用何种应用虚拟化技术(LXC, MicroVM, LibOS),都能够通过Kubernetes统一编排。 当前使用最多的是docker,其次是cri-o。docker与crio结合kata-runtime都能够支持多种应用虚拟化技术混合编排的场景,如LXC与MicroVM混合编排。 docker(now): Moby 公司贡献的 docker 相关部件,当前主流使用的模式。 docker(daemon) 提供对外访问的API与CLI(docker client) containerd 提供与 kubelet 对接的 CRI 接口实现 shim负责将Pod桥接到Host namespace。 cri-o: 由 RedHat/Intel/SUSE/IBM/Hyper 公司贡献的实现了CRI接口的符合OCI规范的运行时,当前包括 runc 与 kata-runtime ,也就是说使用 cir-o 可以同时运行LXC容器与MicroVM容器,具体在Kata介绍中有详细说明。 CRI-O: 实现了CRI接口的进程,与 kubelet 交互 crictl: 类似 docker 的命令行工具 conmon: Pod监控进程 other cri runtimes: 其他的一些cri实现,目前没有大规模应用到生产环境。 容器与传统虚拟化差异 容器(container)的技术构成 前面主要讲到的是容器与编排,包括CRI接口的各种实现,我们把容器领域的规范归纳为南向与北向两部分,CRI属于北向接口规范,对接编排系统,OCI就属于南向接口规范,实现应用虚拟化。 简单来讲,可以这么定义容器: 容器(container) ~= 应用打包(build) + 应用分发(ship) + 应用运行/资源隔离(run)。 build-ship-run 的内容都被定义到了OCI规范中,因此也可以这么定义容器: 容器(container) == OCI规范 OCI规范包含两部分,镜像规范与运行时规范。简要的说,要实现一个OCI的规范,需要能够下载镜像并解压镜像到文件系统上组成成一个文件目录结构,运行时工具能够理解这个目录结构并基于此目录结构管理(创建/启动/停止/删除)进程。 容器(container)的技术构成就是实现OCI规范的技术集合。 对于不同的操作系统(Linux/Windows),OCI规范的实现技术不同,当前docker的实现,支持Windows与Linux与MacOS操作系统。当前使用最广的是Linux系统,OCI的实现,在Linux上组成容器的主要技术: chroot: 通过分层文件系统堆叠出容器进程的rootfs,然后通过chroot设置容器进程的根文件系统为堆叠出的rootfs。 cgroups: 通过cgroups技术隔离容器进程的cpu/内存资源。 namesapce: 通过pid, uts, mount, network, user namesapce 分别隔离容器进程的进程ID,时间,文件系统挂载,网络,用户资源。 网络虚拟化: 容器进程被放置到独立的网络命名空间,通过Linux网络虚拟化veth, macvlan, bridge等技术连接主机网络与容器虚拟网络。 存储驱动: 本地文件系统,使用容器镜像分层文件堆叠的各种实现驱动,当前推荐的是overlay2。 广义的容器还包含容器编排,即当下很火热的Kubernetes。Kubernetes为了把控容器调度的生态,发布了CRI规范,通过CRI规范解耦Kubelet与容器,只要实现了CRI接口,都可以与Kubelet交互,从而被Kubernetes调度。OCI规范的容器实现与CRI标准接口对接的实现是CRI-O。 容器与虚拟机差异对比 容器与虚拟机的差异可以总结为2点:应用打包与分发的差异,应用资源隔离的差异。当然,导致这两点差异的根基是容器是以应用为中心来设计的,而虚拟化是以资源为中心来设计的,本文对比容器与虚拟机的差异,更多的是站在应用视角来对比。 从3个方面对比差异:资源隔离,应用打包与分发,延伸的日志/监控/DFX差异。 1.资源隔离 隔离机制差异 容器 虚拟化 mem/cpu cgroup, 使用时候设定 require 与 limit 值 QEMU, KVM network Linux网络虚拟化技术(veth,tap,bridge,macvlan,ipvlan), 跨虚拟机或出公网访问:SNAT/DNAT, service转发:iptables/ipvs, SR-IOV Linux网络虚拟化技术(veth,tap,bridge,macvlan,ipvlan), QEMU, SR-IOV storage 本地存储: 容器存储驱动 本地存储:virtio-blk 差异引入问题与实践建议 应用程序未适配 cgroup 的内存隔离导致问题: 典型的是 JVM 虚拟机,在 JVM 启动时候会根据系统内存自动设置 MaxHeapSize 值,通常是系统内存的1/4,但是 JVM 并未考虑 cgroup 场景,读系统内存时候任然读取主机的内存来设置 MaxHeapSize,这样会导致内存超过 cgroup 限制从而导致进程被 kill 。问题详细阐述与解决建议参考Java inside docker: What you must know to not FAIL。 多次网络虚拟化问题: 如果在虚拟机内使用容器,会多一层网络虚拟化,并加入了SNAT/DNAT技术, iptables/ipvs技术,对网络吞吐量与时延都有影响(具体依赖容器网络方案),对问题定位复杂度变高,同时还需要注意网络内核参数调优。 典型的网络调优参数有:转发表大小 /proc/sys/net/netfilter/nf_conntrack_max 使用iptables 作为service转发实现的时候,在转发规则较多的时候,iptables更新由于需要全量更新导致非常耗时,建议使用ipvs。详细参考[华为云在 K8S 大规模场景下的 Service 性能优化实践](https://zhuanlan.zhihu.com/p/37230013)。 容器IP地址频繁变化不固定,周边系统需要协调适配,包括基于IP地址的白名单或防火墙控制策略需要调整,CMDB记录的应用IP地址需要适配动态IP或者使用服务名替代IP地址。 存储驱动带来的性能损耗: 容器本地文件系统是通过联合文件系统方式堆叠出来的,当前主推与默认提供的是overlay2驱动,这种模式应用写本地文件系统文件或修改已有文件,使用Copy-On-Write方式,也就是会先拷贝源文件到可写层然后修改,如果这种操作非常频繁,建议使用 volume 方式。 2.应用打包与分发 应用打包/分发/调度差异 容器 虚拟化 打包 打包既部署 一般不会把应用程序与虚拟机打包在一起,通过部署系统部署应用 分发 使用镜像仓库存储与分发 使用文件存储 调度运行 使用K8S亲和/反亲和调度策略 使用部署系统的调度能力 差异引入问题与实践建议 部署提前到构建阶段,应用需要支持动态配置与静态程序分离;如果在传统部署脚本中依赖外部动态配置,这部分需要做一些调整。 打包格式发生变化,制作容器镜像需要注意安全/效率因素,可参考Dockerfile最佳实践 容器镜像存储与分发是按layer来组织的,镜像在传输过程中放篡改的方式是传统软件包有差异。 3.监控/日志/DFX 差异 容器 虚拟化 监控 cpu/mem的资源上限是cgroup定义的;containerd/shim/docker-daemon等进程的监控 传统进程监控 日志采集 stdout/stderr日志采集方式变化;日志持久化需要挂载到volume;进程会被随机调度到其他节点导致日志需要实时采集否则分散很难定位 传统日志采集 问题定位 进程down之后自动拉起会导致问题定位现场丢失;无法停止进程来定位问题因为停止即删除实例 传统问题定位手段 差异引入问题实践与建议 使用成熟的监控工具,运行在docker中的应用使用cadvisor+prometheus实现采集与警报,cadvisor中预置了常用的监控指标项 对于docker管理进程(containerd/shim/docker-daemon)也需要一并监控 使用成熟的日志采集工具,如果已有日志采集Agent,则可以考虑将日志文件挂载到volume后由Agent采集;需要注意的是stderr/stdout输出也要一并采集 如果希望容器内应用进程退出后保留现场定位问题,则可以将Pod的restartPolicy设置为never,进程退出后进程文件都还保留着(/var/lib/docker/containers)。但是这么做的话需要进程没有及时恢复,会影响业务,需要自己实现进程重拉起。 团队配合 与周边的开发团队、架构团队、测试团队、运维团队评审并交流方案,与周边团队达成一致。 落地策略与注意事项 逐步演进过程中网络互通 根据当前已经存在的基础实施情况,选择容器化落地策略。通常使用逐步演进的方式,由于容器化引入了独立的网络namespace导致容器与传统虚拟机进程网络隔离,逐步演进过程中如何打通隔离的网络是最大的挑战。 分两种场景讨论: 不同服务集群之间使用VIP模式互通: 这种模式相对简单,基于VIP做灰度发布。 不同服务集群之间使用微服务点对点模式互通(SpringCloud/ServiceComb/Dubbo都是这一类): 这种模式相对复杂,在逐步容器化过程中,要求容器网络与传统虚拟机网络能够互通(难点是在虚拟机进程内能够直接访问到容器网络的IP地址),当前解决这个问题有几种方法。 自建Kubernetes场景,可使用开源的kube-router,kube-router 使用BGP协议实现容器网络与传统虚拟机网络之间互通,要求网络交换机支持BGP协议。 使用云厂商托管Kubernetes场景,选择云厂商提供的VPC-Router互通的网络插件,如阿里云的Terway网络插件, 华为云的Underlay网络模式。 选择物理机还是虚拟机 选择物理机运行容器还是虚拟机运行容器,需要结合基础设施与业务隔离性要求综合考虑。分两种场景:自建IDC、租用公有云。 自建IDC: 理想情况是使用物理机组成一个大集群,根据业务诉求,对资源保障与安全性要求高的应用,使用MicorVM方式隔离;普通应用使用LXC方式隔离。所有物理机在一个大集群内,方便削峰填谷提升资源利用率。 租用公有云:当前公有云厂家提供的裸金属服务价格较贵且只能包周期,使用裸金属性价比并不高,使用虚拟机更合适。 集群规模与划分 选择集群时候,是多个应用共用一个大集群,还是按应用分组分成多个小集群呢?我们把节点规模数量>=1000的定义为大集群,节点数<1000的定义为小集群。 大集群的优点是资源池共享容器,方便资源调度(削峰填谷);缺点是随着节点数量与负载数量的增多,会引入管理性能问题(需要量化): DNS 解析表变大,增加/删除 Service 或 增加/删除 Endpoint 导致DNS表刷新慢 K8S Service 转发表变大,导致工作负载增加/删除刷新iptables/ipvs记录变慢 etcd 存储空间变大,如果加上ConfigMap,可能导致 etcd 访问时延增加 小集群的优点是不会有管理性能问题,缺点是会导致资源碎片化,不容易共享。共享分两种情况: 应用之间削峰填谷:目前无法实现 计算任务与应用之间削峰填谷:由于计算任务是短时任务,可以通过上层的任务调度软件,在多个集群之间分发计算任务,从而达到集群之间资源共享的目的。 选择集群规模的时候,可以参考上述分析,结合实际情况选择适合的集群划分。 Helm? Helm是为了解决K8S管理对象散碎的问题,在K8S中并没有"应用"的概念,只有一个个散的对象(Deployment, ConfigMap, Service, etc),而一个"应用"是多个对象组合起来的,且这些对象之间还可能存在一定的版本配套关系。 Helm 通过将K8S多个对象打包为一个包并标注版本号形成一个"应用",通过 Helm 管理进程部署/升级这个"应用"。这种方式解决了一些问题(应用分发更方便)同时也引入了一些问题(引入Helm增加应用发布/管理复杂度、在K8S修改了对象后如何同步到Helm)。对于是否需要使用Helm,建议如下: 在自运维模式下不使用Helm: 自运维模式下,很多场景是开发团队交付一个运行包,运维团队负责部署与配置下发,内部通过兼容性或软件包与配置版本配套清单、管理软件包与配置的配套关系。 在交付软件包模式下使用Helm: 交付软件包模式下,Helm 这种把散碎组件组装为一个应用的模式比较适合,使用Helm实现软件包分发/部署/升级场比较简单。 Reference DOCKER vs LXC vs VIRTUAL MACHINES Cgroup与LXC简介 Introducing Container Runtime Interface (CRI) in Kubernetes frakti rkt appc-spec OCI 和 runc:容器标准化和 docker Linux 容器技术史话:从 chroot 到未来 Linux Namespace和Cgroup Java inside docker: What you must know to not FAIL QEMU,KVM及QEMU-KVM介绍 kvm libvirt qemu实践系列(一)-kvm介绍 KVM 介绍(4):I/O 设备直接分配和 SR-IOV [KVM PCI/PCIe Pass-Through SR-IOV] prometheus-book 到底什么是Unikernel? The Rise and Fall of the Operating System The Design and Implementation of the Anykernel and Rump Kernels UniKernel Unikernel:从不入门到入门 OSv 京东如何打造K8s全球最大集群支撑万亿电商交易 Cloud Native App Hub 更多云最佳实践 https://best.practices.cloud 本篇文章为转载内容。原文链接:https://blog.csdn.net/sinat_33155975/article/details/118013855。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-17 15:03:28
225
转载
转载文章
...们关注软件更新与补丁管理的重要性。同时,对于数据库系统内部机制的理解也至关重要,比如MySQL中的pipes_as_concat模式下字符串拼接符“||”的特殊作用,它警示开发者在构建查询时需考虑潜在的安全风险,并合理配置数据库参数以增强安全性。 总的来说,无论是针对传统SQL注入手法的深入探究,还是紧跟CVE公告及时发现并修复新出现的安全漏洞,CTF比赛所涵盖的各种实战演练都是广大网络安全从业者及爱好者丰富知识库、提高实战技能的有效途径。同时,这也提醒我们应时刻保持警惕,密切关注业界动态,不断提升自身的安全防护能力,确保在网络空间的攻防对抗中立于不败之地。
2023-11-13 21:30:33
303
转载
转载文章
...dowmanager管理的surface作为生产方产生页面,交由surfaceflinger进行合成。 VSYNC Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,VSYNC是一种在PC上很早就有应用,可以理解为一种定时中断技术。 tearing 问题: 早期的 Android 是没有 vsync 机制的,CPU 和 GPU 的配合也比较混乱,这也造成著名的 tearing 问题,即 CPU/GPU 直接更新正在显示的屏幕 buffer 造成画面撕裂。 后续 Android 引入了双缓冲机制,但是 buffer 的切换也需要一个比较合适的时机,也就是屏幕扫描完上一帧后的时机,这也就是引入 vsync 的原因。 早先一般的屏幕刷新率是 60fps,所以每个 vsync 信号的间隔也是 16ms,不过随着技术的更迭以及厂商对于流畅性的追求,越来越多 90fps 和 120fps 的手机面世,相对应的间隔也就变成了 11ms 和 8ms。 VSYNC信号种类: 1.屏幕产生的硬件VSYNC:硬件VSYNC是一种脉冲信号,起到开关和触发某种操作的作用。 2.由SurfaceFlinger将其转成的软件VSYNC信号,经由Binder传递给Choreographer Choreographer: 编舞者,用于注册VSYNC信号并接收VSYNC信号回调,当内部接收到这个信号时最终会调用到doFrame进行帧的绘制操作。 Choreographer在系统中流程: 如何通过Choreographer计算掉帧情况:原理就是: 通过给Choreographer设置FrameCallback,在每次绘制前后看时间差是16.6ms的多少倍,即为前后掉帧率。 使用方式如下: //Application.javapublic void onCreate() {super.onCreate();//在Application中使用postFrameCallbackChoreographer.getInstance().postFrameCallback(new FPSFrameCallback(System.nanoTime()));}public class FPSFrameCallback implements Choreographer.FrameCallback {private static final String TAG = "FPS_TEST";private long mLastFrameTimeNanos = 0;private long mFrameIntervalNanos;public FPSFrameCallback(long lastFrameTimeNanos) {mLastFrameTimeNanos = lastFrameTimeNanos;mFrameIntervalNanos = (long)(1000000000 / 60.0);}@Overridepublic void doFrame(long frameTimeNanos) {//初始化时间if (mLastFrameTimeNanos == 0) {mLastFrameTimeNanos = frameTimeNanos;}final long jitterNanos = frameTimeNanos - mLastFrameTimeNanos;if (jitterNanos >= mFrameIntervalNanos) {final long skippedFrames = jitterNanos / mFrameIntervalNanos;if(skippedFrames>30){//丢帧30以上打印日志Log.i(TAG, "Skipped " + skippedFrames + " frames! "+ "The application may be doing too much work on its main thread.");} }mLastFrameTimeNanos=frameTimeNanos;//注册下一帧回调Choreographer.getInstance().postFrameCallback(this);} } UI绘制全路径分析: 有了前面几个概念,这里我们让SurfaceFlinger结合View的绘制流程用一张图来表达整个绘制流程: 生产者:APP方构建Surface的过程。 消费者:SurfaceFlinger UI绘制全路径分析卡顿原因: 接下来,我们逐个分析,看看都会有哪些原因可能造成卡顿: 1.渲染流程 1.Vsync 调度:这个是起始点,但是调度的过程会经过线程切换以及一些委派的逻辑,有可能造成卡顿,但是一般可能性比较小,我们也基本无法介入; 2.消息调度:主要是 doframe Message 的调度,这就是一个普通的 Handler 调度,如果这个调度被其他的 Message 阻塞产生了时延,会直接导致后续的所有流程不会被触发 3.input 处理:input 是一次 Vsync 调度最先执行的逻辑,主要处理 input 事件。如果有大量的事件堆积或者在事件分发逻辑中加入大量耗时业务逻辑,会造成当前帧的时长被拉大,造成卡顿,可以尝试通过事件采样的方案,减少 event 的处理 4.动画处理:主要是 animator 动画的更新,同理,动画数量过多,或者动画的更新中有比较耗时的逻辑,也会造成当前帧的渲染卡顿。对动画的降帧和降复杂度其实解决的就是这个问题; 5.view 处理:主要是接下来的三大流程,过度绘制、频繁刷新、复杂的视图效果都是此处造成卡顿的主要原因。比如我们平时所说的降低页面层级,主要解决的就是这个问题; 6.measure/layout/draw:view 渲染的三大流程,因为涉及到遍历和高频执行,所以这里涉及到的耗时问题均会被放大,比如我们会降不能在 draw 里面调用耗时函数,不能 new 对象等等; 7.DisplayList 的更新:这里主要是 canvas 和 displaylist 的映射,一般不会存在卡顿问题,反而可能存在映射失败导致的显示问题; 8.OpenGL 指令转换:这里主要是将 canvas 的命令转换为 OpenGL 的指令,一般不存在问题 9.buffer 交换:这里主要指 OpenGL 指令集交换给 GPU,这个一般和指令的复杂度有关 10.GPU 处理:顾名思义,这里是 GPU 对数据的处理,耗时主要和任务量和纹理复杂度有关。这也就是我们降低 GPU 负载有助于降低卡顿的原因; 11.layer 合成:Android P 修改了 Layer 的计算方法 , 把这部分放到了 SurfaceFlinger 主线程去执行, 如果后台 Layer 过多, 就会导致 SurfaceFlinger 在执行 rebuildLayerStacks 的时候耗时 , 导致 SurfaceFlinger 主线程执行时间过长。 可以选择降低Surface层级来优化卡顿。 12.光栅化/Display:这里暂时忽略,底层系统行为; Buffer 切换:主要是屏幕的显示,这里 buffer 的数量也会影响帧的整体延迟,不过是系统行为,不能干预。 2.系统负载 内存:内存的吃紧会直接导致 GC 的增加甚至 ANR,是造成卡顿的一个不可忽视的因素; CPU:CPU 对卡顿的影响主要在于线程调度慢、任务执行的慢和资源竞争,比如 1.降频会直接导致应用卡顿; 2.后台活动进程太多导致系统繁忙,cpu \ io \ memory 等资源都会被占用, 这时候很容易出现卡顿问题 ,这种情况比较常见,可以使用dumpsys cpuinfo查看当前设备的cpu使用情况: 3.主线程调度不到 , 处于 Runnable 状态,这种情况比较少见 4.System 锁:system_server 的 AMS 锁和 WMS 锁 , 在系统异常的情况下 , 会变得非常严重 , 如下图所示 , 许多系统的关键任务都被阻塞 , 等待锁的释放 , 这时候如果有 App 发来的 Binder 请求带锁 , 那么也会进入等待状态 , 这时候 App 就会产生性能问题 ; 如果此时做 Window 动画 , 那么 system_server 的这些锁也会导致窗口动画卡顿 GPU:GPU 的影响见渲染流程,但是其实还会间接影响到功耗和发热; 功耗/发热:功耗和发热一般是不分家的,高功耗会引起高发热,进而会引起系统保护,比如降频、热缓解等,间接的导致卡顿。 如何监控卡顿 线下监控: 我们知道卡顿问题的原因错综复杂,但最终都可以反馈到CPU使用率上来 1.使用dumpsys cpuinfo命令 这个命令可以获取当时设备cpu使用情况,我们可以在线下通过重度使用应用来检测可能存在的卡顿点 A8S:/ $ dumpsys cpuinfoLoad: 1.12 / 1.12 / 1.09CPU usage from 484321ms to 184247ms ago (2022-11-02 14:48:30.793 to 2022-11-02 14:53:30.866):2% 1053/scanserver: 0.2% user + 1.7% kernel0.6% 934/system_server: 0.4% user + 0.1% kernel / faults: 563 minor0.4% 564/signserver: 0% user + 0.4% kernel0.2% 256/ueventd: 0.1% user + 0% kernel / faults: 320 minor0.2% 474/surfaceflinger: 0.1% user + 0.1% kernel0.1% 576/vendor.sprd.hardware.gnss@2.0-service: 0.1% user + 0% kernel / faults: 54 minor0.1% 286/logd: 0% user + 0% kernel / faults: 10 minor0.1% 2821/com.allinpay.appstore: 0.1% user + 0% kernel / faults: 1312 minor0.1% 447/android.hardware.health@2.0-service: 0% user + 0% kernel / faults: 1175 minor0% 1855/com.smartpos.dataacqservice: 0% user + 0% kernel / faults: 755 minor0% 2875/com.allinpay.appstore:pushcore: 0% user + 0% kernel / faults: 744 minor0% 1191/com.android.systemui: 0% user + 0% kernel / faults: 70 minor0% 1774/com.android.nfc: 0% user + 0% kernel0% 172/kworker/1:2: 0% user + 0% kernel0% 145/irq/24-70900000: 0% user + 0% kernel0% 575/thermald: 0% user + 0% kernel / faults: 300 minor... 2.CPU Profiler 这个工具是AS自带的CPU性能检测工具,可以在PC上实时查看我们CPU使用情况。 AS提供了四种Profiling Model配置: 1.Sample Java Methods:在应用程序基于Java的代码执行过程中,频繁捕获应用程序的调用堆栈 获取有关应用程序基于Java的代码执行的时间和资源使用情况信息。 2.Trace java methods:在运行时对应用程序进行检测,以在每个方法调用的开始和结束时记录时间戳。收集时间戳并进行比较以生成方法跟踪数据,包括时序信息和CPU使用率。 请注意与检测每种方法相关的开销会影响运行时性能,并可能影响性能分析数据。对于生命周期相对较短的方法,这一点甚至更为明显。此外,如果您的应用在短时间内执行大量方法,则探查器可能会很快超过其文件大小限制,并且可能无法记录任何进一步的跟踪数据。 3.Sample C/C++ Functions:捕获应用程序本机线程的示例跟踪。要使用此配置,您必须将应用程序部署到运行Android 8.0(API级别26)或更高版本的设备。 4.Trace System Calls:捕获细粒度的详细信息,使您可以检查应用程序与系统资源的交互方式 您可以检查线程状态的确切时间和持续时间,可视化CPU瓶颈在所有内核中的位置,并添加自定义跟踪事件进行分析。在对性能问题进行故障排除时,此类信息可能至关重要。要使用此配置,您必须将应用程序部署到运行Android 7.0(API级别24)或更高版本的设备。 使用方式: Debug.startMethodTracing("");// 需要检测的代码片段...Debug.stopMethodTracing(); 优点:有比较全面的调用栈以及图像化方法时间显示,包含所有线程的情况 缺点:本身也会带来一点的性能开销,可能会带偏优化方向 火焰图:可以显示当前应用的方法堆栈: 3.Systrace Systrace在前面一篇分析启动优化的文章讲解过 这里我们简单来复习下: Systrace用来记录当前应用的系统以及应用(使用Trace类打点)的各阶段耗时信息包括绘制信息以及CPU信息等。 使用方式: Trace.beginSection("MyApp.onCreate_1");alt(200);Trace.endSection(); 在命令行中: python systrace.py -t 5 sched gfx view wm am app webview -a "com.chinaebipay.thirdcall" -o D:\trac1.html 记录的方法以及CPU中的耗时情况: 优点: 1.轻量级,开销小,CPU使用率可以直观反映 2.右侧的Alerts能够根据我们应用的问题给出具体的建议,比如说,它会告诉我们App界面的绘制比较慢或者GC比较频繁。 4.StrictModel StrictModel是Android提供的一种运行时检测机制,用来帮助开发者自动检测代码中不规范的地方。 主要和两部分相关: 1.线程相关 2.虚拟机相关 基础代码: private void initStrictMode() {// 1、设置Debug标志位,仅仅在线下环境才使用StrictModeif (DEV_MODE) {// 2、设置线程策略StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode.detectDiskReads().detectDiskWrites().detectNetwork() // or .detectAll() for all detectable problems.penaltyLog() //在Logcat 中打印违规异常信息// .penaltyDialog() //也可以直接跳出警报dialog// .penaltyDeath() //或者直接崩溃.build());// 3、设置虚拟机策略StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects()// 给NewsItem对象的实例数量限制为1.setClassInstanceLimit(NewsItem.class, 1).detectLeakedClosableObjects() //API等级11.penaltyLog().build());} } 线上监控: 线上需要自动化的卡顿检测方案来定位卡顿,它能记录卡顿发生时的场景。 自动化监控原理: 采用拦截消息调度流程,在消息执行前埋点计时,当耗时超过阈值时,则认为是一次卡顿,会进行堆栈抓取和上报工作 首先,我们看下Looper用于执行消息循环的loop()方法,关键代码如下所示: / Run the message queue in this thread. Be sure to call {@link quit()} to end the loop./public static void loop() {...for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {// 1logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}...try {// 2 msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);} }...if (logging != null) {// 3logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);} 在Looper的loop()方法中,在其执行每一个消息(注释2处)的前后都由logging进行了一次打印输出。可以看到,在执行消息前是输出的">>>>> Dispatching to “,在执行消息后是输出的”<<<<< Finished to ",它们打印的日志是不一样的,我们就可以由此来判断消息执行的前后时间点。 具体的实现可以归纳为如下步骤: 1、首先,我们需要使用Looper.getMainLooper().setMessageLogging()去设置我们自己的Printer实现类去打印输出logging。这样,在每个message执行的之前和之后都会调用我们设置的这个Printer实现类。 2、如果我们匹配到">>>>> Dispatching to "之后,我们就可以执行一行代码:也就是在指定的时间阈值之后,我们在子线程去执行一个任务,这个任务就是去获取当前主线程的堆栈信息以及当前的一些场景信息,比如:内存大小、电脑、网络状态等。 3、如果在指定的阈值之内匹配到了"<<<<< Finished to ",那么说明message就被执行完成了,则表明此时没有产生我们认为的卡顿效果,那我们就可以将这个子线程任务取消掉。 这里我们使用blockcanary来做测试: BlockCanary APM是一个非侵入式的性能监控组件,可以通过通知的形式弹出卡顿信息。它的原理就是我们刚刚讲述到的卡顿监控的实现原理。 使用方式: 1.导入依赖 implementation 'com.github.markzhai:blockcanary-android:1.5.0' Application的onCreate方法中开启卡顿监控 // 注意在主进程初始化调用BlockCanary.install(this, new AppBlockCanaryContext()).start(); 3.继承BlockCanaryContext类去实现自己的监控配置上下文类 public class AppBlockCanaryContext extends BlockCanaryContext {....../ 指定判定为卡顿的阈值threshold (in millis), 你可以根据不同设备的性能去指定不同的阈值 @return threshold in mills/public int provideBlockThreshold() {return 1000;}....} 4.在Activity的onCreate方法中执行一个耗时操作 try {Thread.sleep(4000);} catch (InterruptedException e) {e.printStackTrace();} 5.结果: 可以看到一个和LeakCanary一样效果的阻塞可视化堆栈图 那有了BlockCanary的方法耗时监控方式是不是就可以解百愁了呢,呵呵。有那么容易就好了 根据原理:我们拿到的是msg执行前后的时间和堆栈信息,如果msg中有几百上千个方法,就无法确认到底是哪个方法导致的耗时,也有可能是多个方法堆积导致。 这就导致我们无法准确定位哪个方法是最耗时的。如图中:堆栈信息是T2的,而发生耗时的方法可能是T1到T2中任何一个方法甚至是堆积导致。 那如何优化这块? 这里我们采用字节跳动给我们提供的一个方案:基于 Sliver trace 的卡顿监控体系 Sliver trace 整体流程图: 主要包含两个方面: 检测方案: 在监控卡顿时,首先需要打开 Sliver 的 trace 记录能力,Sliver 采样记录 trace 执行信息,对抓取到的堆栈进行 diff 聚合和缓存。 同时基于我们的需要设置相应的卡顿阈值,以 Message 的执行耗时为衡量。对主线程消息调度流程进行拦截,在消息开始分发执行时埋点,在消息执行结束时计算消息执行耗时,当消息执行耗时超过阈值,则认为产生了一次卡顿。 堆栈聚合策略: 当卡顿发生时,我们需要为此次卡顿准备数据,这部分工作是在端上子线程中完成的,主要是 dump trace 到文件以及过滤聚合要上报的堆栈。分为以下几步: 1.拿到缓存的主线程 trace 信息并 dump 到文件中。 2.然后从文件中读取 trace 信息,按照数据格式,从最近的方法栈向上追溯,找到当前 Message 包含的全部 trace 信息,并将当前 Message 的完整 trace 写入到待上传的 trace 文件中,删除其余 trace 信息。 3.遍历当前 Message trace,按照(Method 执行耗时 > Method 耗时阈值 & Method 耗时为该层堆栈中最耗时)为条件过滤出每一层函数调用堆栈的最长耗时函数,构成最后要上报的堆栈链路,这样特征堆栈中的每一步都是最耗时的,且最底层 Method 为最后的耗时大于阈值的 Method。 之后,将 trace 文件和堆栈一同上报,这样的特征堆栈提取策略保证了堆栈聚合的可靠性和准确性,保证了上报到平台后堆栈的正确合理聚合,同时提供了进一步分析问题的 trace 文件。 可以看到字节给的是一整套监控方案,和前面BlockCanary不同之处就在于,其是定时存储堆栈,缓存,然后使用diff去重的方式,并上传到服务器,可以最大限度的监控到可能发生比较耗时的方法。 开发中哪些习惯会影响卡顿的发生 1.布局太乱,层级太深。 1.1:通过减少冗余或者嵌套布局来降低视图层次结构。比如使用约束布局代替线性布局和相对布局。 1.2:用 ViewStub 替代在启动过程中不需要显示的 UI 控件。 1.3:使用自定义 View 替代复杂的 View 叠加。 2.主线程耗时操作 2.1:主线程中不要直接操作数据库,数据库的操作应该放在数据库线程中完成。 2.2:sharepreference尽量使用apply,少使用commit,可以使用MMKV框架来代替sharepreference。 2.3:网络请求回来的数据解析尽量放在子线程中,不要在主线程中进行复制的数据解析操作。 2.4:不要在activity的onResume和onCreate中进行耗时操作,比如大量的计算等。 2.5:不要在 draw 里面调用耗时函数,不能 new 对象 3.过度绘制 过度绘制是同一个像素点上被多次绘制,减少过度绘制一般减少布局背景叠加等方式,如下图所示右边是过度绘制的图片。 4.列表 RecyclerView使用优化,使用DiffUtil和notifyItemDataSetChanged进行局部更新等。 5.对象分配和回收优化 自从Android引入 ART 并且在Android 5.0上成为默认的运行时之后,对象分配和垃圾回收(GC)造成的卡顿已经显著降低了,但是由于对象分配和GC有额外的开销,它依然又可能使线程负载过重。 在一个调用不频繁的地方(比如按钮点击)分配对象是没有问题的,但如果在在一个被频繁调用的紧密的循环里,就需要避免对象分配来降低GC的压力。 减少小对象的频繁分配和回收操作。 好了,关于卡顿优化的问题就讲到这里,下篇文章会对卡顿中的ANR情况的处理,这里做个铺垫。 如果喜欢我的文章,欢迎关注我的公众号。 点击这看原文链接: 参考 Android卡顿检测及优化 一文读懂直播卡顿优化那些事儿 “终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解! 深入探索Android卡顿优化(上) 西瓜卡顿 & ANR 优化治理及监控体系建设 5376)] 参考 Android卡顿检测及优化 一文读懂直播卡顿优化那些事儿 “终于懂了” 系列:Android屏幕刷新机制—VSync、Choreographer 全面理解! 深入探索Android卡顿优化(上) 西瓜卡顿 & ANR 优化治理及监控体系建设 本篇文章为转载内容。原文链接:https://blog.csdn.net/yuhaibing111/article/details/127682399。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-26 08:05:57
214
转载
转载文章
...异步方法的调用和状态管理。微软在.NET生态系统中提倡采用异步编程模型,利用C的事件和委托机制,能够简化异步操作的处理流程,提高程序性能和响应速度。 对于设计模式层面的理解,委托与观察者模式(Observer Pattern)紧密相连,它允许对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。结合最新的.NET技术趋势,诸如Reactive Extensions (Rx.NET)等库更是将这种模式发扬光大,借助LINQ风格的查询操作符和事件流处理,让委托在实时数据流处理领域展现出了强大的功能。 总之,深入掌握C中的委托和事件不仅有助于日常开发工作的效率提升,更能紧跟现代软件工程的发展潮流,充分利用最新的技术和框架优势,构建出高性能、高可维护性的应用程序。而不断跟进官方文档、社区讨论和技术博客,则是深化此类主题理解和实践运用的有效途径。
2023-10-05 16:02:19
80
转载
转载文章
...制器 Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod。 控制器的种类 在kubernetes有很多种类型的pod控制器,每种都有自己的使用场景 ReplicationController:比较原始的pod控制器,已经被废弃,由ReplicaSet替代 ReplicaSet:保证副本数量一直维持在期望值,并支持pod数量扩缩容,镜像版本升级 Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、回退版本 Horizontal Pod Autoscaler:可以根据集群负载自动水平调整Pod的数量,实现削峰填谷 DaemonSet:在集群中的指定Node上运行且仅运行一个副本,一般用于守护进程类的任务 Job:它创建出来的pod只要完成任务就立即退出,不需要重启或重建,用于执行一次性任务 Cronjob:它创建的Pod负责周期性任务控制,不需要持续后台运行,可以理解为是定时任务; StatefulSet:管理有状态应用 1、ReplicaSet 简称为RS,主要的作用是保证一定数量的pod能够正常运行,它会持续监听这些pod的运行状态,提供了以下功能 自愈能力: 重启 :当某节点中的pod运行过程中出现问题导致无法启动时,k8s会不断重启,直到可用状态为止 故障转移:当正在运行中pod所在的节点发生故障或者宕机时,k8s会选择集群中另一个可用节点,将pod运行到可用节点上; pod数量的扩缩容:pod副本的扩容和缩容 镜像升降级:支持镜像版本的升级和降级; 配置模板 rs的所有配置如下 apiVersion: apps/v1 版本号kind: ReplicaSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: rsspec: 详情描述replicas: 3 副本数量selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则,key就是label的key,values的值是个数组,意思是标签值必须是此数组中的其中一个才能匹配上;- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels: 这里的标签必须和上面的matchLabels一致,将他们关联起来app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建一个ReplicaSet 新建一个文件 rs.yaml,内容如下 apiVersion: apps/v1kind: ReplicaSet pod控制器metadata: 元数据name: pc-replicaset 名字namespace: dev 名称空间spec:replicas: 3 副本数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podtemplate: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 kubectl create -f rs.yaml 获取replicaset kubectl get replicaset -n dev 2、扩缩容 刚刚我们已经用第一种方式创建了一个replicaSet,现在就基于原来的rs进行扩容,原来的副本数量是3个,现在我们将其扩到6个,做法也很简单,运行编辑命令 第一种方式: scale 使用scale命令实现扩缩容,后面--replicas=n直接指定目标数量即可kubectl scale rs pc-replicaset --replicas=2 -n dev 第二种方式:使用edit命令编辑rs 这种方式相当于使用vi编辑修改yaml配置的内容,进去后将replicas的值改为1,保存后自动生效kubectl edit rs pc-replicaset -n dev 3、镜像版本变更 第一种方式:scale kubectl scale rs pc-replicaset nginx=nginx:1.71.2 -n dev 第二种方式:edit 这种方式相当于使用vi编辑修改yaml配置的内容,进去后将nginx的值改为nginx:1.71.2,保存后自动生效kubectl edit rs pc-replicaset -n dev 4、删除rs 第一种方式kubectl delete -f rs.yaml 第二种方式 ,如果想要只删rs,但不删除pod,可在删除时加上--cascade=false参数(不推荐)kubectl delete rs pc-replicaset -n dev --cascade=false 2、Deployment k8s v1.2版本后加入Deployment;这种控制器不直接控制pod,而是通过管理ReplicaSet来间接管理pod;也就是Deployment管理ReplicaSet,ReplicaSet管理pod;所以 Deployment 比 ReplicaSet 功能更加强大 当我们创建了一个Deployment之后,也会自动创建一个ReplicaSet 功能 支持ReplicaSet 的所有功能 支持发布的停止、继续 支持版本的滚动更新和回退功能 配置模板 新建文件 apiVersion: apps/v1 版本号kind: Deployment 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: deployspec: 详情描述replicas: 3 副本数量revisionHistoryLimit: 3 保留历史版本的数量,默认10,内部通过保留rs来实现paused: false 暂停部署,默认是falseprogressDeadlineSeconds: 600 部署超时时间(s),默认是600strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxSurge: 30% 最大额外可以存在的副本数,可以为百分比,也可以为整数maxUnavailable: 30% 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建和删除Deployment 创建pc-deployment.yaml,内容如下: apiVersion: apps/v1kind: Deployment metadata:name: pc-deploymentnamespace: devspec: replicas: 3selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 创建和查看 创建deployment,--record=true 表示记录整个deployment更新过程[root@k8s-master01 ~] kubectl create -f pc-deployment.yaml --record=truedeployment.apps/pc-deployment created 查看deployment READY 可用的/总数 UP-TO-DATE 最新版本的pod的数量 AVAILABLE 当前可用的pod的数量[root@k8s-master01 ~] kubectl get deploy pc-deployment -n devNAME READY UP-TO-DATE AVAILABLE AGEpc-deployment 3/3 3 3 15s 查看rs 发现rs的名称是在原来deployment的名字后面添加了一个10位数的随机串[root@k8s-master01 ~] kubectl get rs -n devNAME DESIRED CURRENT READY AGEpc-deployment-6696798b78 3 3 3 23s 查看pod[root@k8s-master01 ~] kubectl get pods -n devNAME READY STATUS RESTARTS AGEpc-deployment-6696798b78-d2c8n 1/1 Running 0 107spc-deployment-6696798b78-smpvp 1/1 Running 0 107spc-deployment-6696798b78-wvjd8 1/1 Running 0 107s 删除deployment 删除deployment,其下的rs和pod也将被删除kubectl delete -f pc-deployment.yaml 2、扩缩容 deployment的扩缩容和 ReplicaSet 的扩缩容一样,只需要将rs或者replicaSet改为deployment即可,具体请参考上面的 ReplicaSet 扩缩容 3、镜像更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 镜像更新策略有2种 滚动更新(RollingUpdate):(默认值),杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod 重建更新(Recreate):在创建出新的Pod之前会先杀掉所有已存在的Pod strategy:指定新的Pod替换旧的Pod的策略, 支持两个属性:type:指定策略类型,支持两种策略Recreate:在创建出新的Pod之前会先杀掉所有已存在的PodRollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本PodrollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性:maxUnavailable:用来指定在升级过程中不可用Pod的最大数量,默认为25%。maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。 重建更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: Recreate 重建更新 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.2 -n devdeployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-5d89bdfbf9-65qcw 1/1 Running 0 31spc-deployment-5d89bdfbf9-w5nzv 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Running 0 31spc-deployment-5d89bdfbf9-xpt7w 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-65qcw 1/1 Terminating 0 41spc-deployment-5d89bdfbf9-w5nzv 1/1 Terminating 0 41spc-deployment-675d469f8b-grn8z 0/1 Pending 0 0spc-deployment-675d469f8b-hbl4v 0/1 Pending 0 0spc-deployment-675d469f8b-67nz2 0/1 Pending 0 0spc-deployment-675d469f8b-grn8z 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-hbl4v 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-67nz2 0/1 ContainerCreating 0 0spc-deployment-675d469f8b-grn8z 1/1 Running 0 1spc-deployment-675d469f8b-67nz2 1/1 Running 0 1spc-deployment-675d469f8b-hbl4v 1/1 Running 0 2s 滚动更新 编辑pc-deployment.yaml,在spec节点下添加更新策略 spec:strategy: 策略type: RollingUpdate 滚动更新策略rollingUpdate:maxSurge: 25% maxUnavailable: 25% 创建deploy进行验证 变更镜像[root@k8s-master01 ~] kubectl set image deployment pc-deployment nginx=nginx:1.17.3 -n dev deployment.apps/pc-deployment image updated 观察升级过程[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-deployment-c848d767-8rbzt 1/1 Running 0 31mpc-deployment-c848d767-h4p68 1/1 Running 0 31mpc-deployment-c848d767-hlmz4 1/1 Running 0 31mpc-deployment-c848d767-rrqcn 1/1 Running 0 31mpc-deployment-966bf7f44-226rx 0/1 Pending 0 0spc-deployment-966bf7f44-226rx 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-226rx 1/1 Running 0 1spc-deployment-c848d767-h4p68 0/1 Terminating 0 34mpc-deployment-966bf7f44-cnd44 0/1 Pending 0 0spc-deployment-966bf7f44-cnd44 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-cnd44 1/1 Running 0 2spc-deployment-c848d767-hlmz4 0/1 Terminating 0 34mpc-deployment-966bf7f44-px48p 0/1 Pending 0 0spc-deployment-966bf7f44-px48p 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-px48p 1/1 Running 0 0spc-deployment-c848d767-8rbzt 0/1 Terminating 0 34mpc-deployment-966bf7f44-dkmqp 0/1 Pending 0 0spc-deployment-966bf7f44-dkmqp 0/1 ContainerCreating 0 0spc-deployment-966bf7f44-dkmqp 1/1 Running 0 2spc-deployment-c848d767-rrqcn 0/1 Terminating 0 34m 至此,新版本的pod创建完毕,就版本的pod销毁完毕 中间过程是滚动进行的,也就是边销毁边创建 4、版本回退 更新 刚刚在创建时加上了--record=true参数,所以在一旦进行了镜像更新,就会新建出一个pod出来,将老的old-pod上的容器全删除,然后在新的new-pod上在新建对应数量的容器,此时old-pod是不会删除的,因为这个old-pod是要进行回退的; 回退 在回退时会将new-pod上的容器全部删除,在将old-pod上恢复原来的容器; 回退命令 kubectl rollout: 版本升级相关功能,支持下面的选项: status 显示当前升级状态 history 显示 升级历史记录 pause 暂停版本升级过程 resume 继续已经暂停的版本升级过程 restart 重启版本升级过程 undo 回滚到上一级版本(可以使用–to-revision回滚到指定版本) 用法 查看当前升级版本的状态kubectl rollout status deploy pc-deployment -n dev 查看升级历史记录kubectl rollout history deploy pc-deployment -n dev 版本回滚 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev 金丝雀发布 Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。 比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。 金丝雀发布不是自动完成的,需要人为手动去操作,才能达到金丝雀发布的标准; 更新deployment的版本,并配置暂停deploymentkubectl set image deploy pc-deployment nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment pc-deployment -n dev 观察更新状态kubectl rollout status deploy pc-deployment -n dev 监控更新的过程kubectl get rs -n dev -o wide 确保更新的pod没问题了,继续更新kubectl rollout resume deploy pc-deployment -n dev 如果有问题,就回退到上个版本回退到上个版本kubectl rollout undo deployment pc-deployment -n dev Horizontal Pod Autoscaler 简称HPA,使用deployment可以手动调整pod的数量来实现扩容和缩容;但是这显然不符合k8s的自动化的定位,k8s期望可以通过检测pod的使用情况,实现pod数量自动调整,于是就有了HPA控制器; HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。比如说我指定了一个规则:当我的cpu利用率达到90%或者内存使用率到达80%的时候,就需要进行调整pod的副本数量,每次添加n个pod副本; 其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析ReplicaSet控制器的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,也就是HPA管理Deployment,Deployment管理ReplicaSet,ReplicaSet管理pod,这是HPA的实现原理。 1、安装metrics-server metrics-server可以用来收集集群中的资源使用情况 安装git[root@k8s-master01 ~] yum install git -y 获取metrics-server, 注意使用的版本[root@k8s-master01 ~] git clone -b v0.3.6 https://github.com/kubernetes-incubator/metrics-server 修改deployment, 注意修改的是镜像和初始化参数[root@k8s-master01 ~] cd /root/metrics-server/deploy/1.8+/[root@k8s-master01 1.8+] vim metrics-server-deployment.yaml按图中添加下面选项hostNetwork: trueimage: registry.cn-hangzhou.aliyuncs.com/google_containers/metrics-server-amd64:v0.3.6args:- --kubelet-insecure-tls- --kubelet-preferred-address-types=InternalIP,Hostname,InternalDNS,ExternalDNS,ExternalIP 2、安装metrics-server [root@k8s-master01 1.8+] kubectl apply -f ./ 3、查看pod运行情况 [root@k8s-master01 1.8+] kubectl get pod -n kube-systemmetrics-server-6b976979db-2xwbj 1/1 Running 0 90s 4、使用kubectl top node 查看资源使用情况 [root@k8s-master01 1.8+] kubectl top nodeNAME CPU(cores) CPU% MEMORY(bytes) MEMORY%k8s-master01 289m 14% 1582Mi 54% k8s-node01 81m 4% 1195Mi 40% k8s-node02 72m 3% 1211Mi 41% [root@k8s-master01 1.8+] kubectl top pod -n kube-systemNAME CPU(cores) MEMORY(bytes)coredns-6955765f44-7ptsb 3m 9Micoredns-6955765f44-vcwr5 3m 8Mietcd-master 14m 145Mi... 至此,metrics-server安装完成 5、 准备deployment和servie 创建pc-hpa-pod.yaml文件,内容如下: apiVersion: apps/v1kind: Deploymentmetadata:name: nginxnamespace: devspec:strategy: 策略type: RollingUpdate 滚动更新策略replicas: 1selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1resources: 资源配额limits: 限制资源(上限)cpu: "1" CPU限制,单位是core数requests: 请求资源(下限)cpu: "100m" CPU限制,单位是core数 创建deployment [root@k8s-master01 1.8+] kubectl run nginx --image=nginx:1.17.1 --requests=cpu=100m -n dev 6、创建service [root@k8s-master01 1.8+] kubectl expose deployment nginx --type=NodePort --port=80 -n dev 7、查看 [root@k8s-master01 1.8+] kubectl get deployment,pod,svc -n devNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/nginx 1/1 1 1 47sNAME READY STATUS RESTARTS AGEpod/nginx-7df9756ccc-bh8dr 1/1 Running 0 47sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/nginx NodePort 10.101.18.29 <none> 80:31830/TCP 35s 8、 部署HPA 创建pc-hpa.yaml文件,内容如下: apiVersion: autoscaling/v1kind: HorizontalPodAutoscalermetadata:name: pc-hpanamespace: devspec:minReplicas: 1 最小pod数量maxReplicas: 10 最大pod数量 ,pod数量会在1~10之间自动伸缩targetCPUUtilizationPercentage: 3 CPU使用率指标,如果cpu使用率达到3%就会进行扩容;为了测试方便,将这个数值调小一些scaleTargetRef: 指定要控制的nginx信息apiVersion: /v1kind: Deploymentname: nginx 创建hpa [root@k8s-master01 1.8+] kubectl create -f pc-hpa.yamlhorizontalpodautoscaler.autoscaling/pc-hpa created 查看hpa [root@k8s-master01 1.8+] kubectl get hpa -n devNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 62s 9、 测试 使用压测工具对service地址192.168.5.4:31830进行压测,然后通过控制台查看hpa和pod的变化 hpa变化 [root@k8s-master01 ~] kubectl get hpa -n dev -wNAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGEpc-hpa Deployment/nginx 0%/3% 1 10 1 4m11spc-hpa Deployment/nginx 0%/3% 1 10 1 5m19spc-hpa Deployment/nginx 22%/3% 1 10 1 6m50spc-hpa Deployment/nginx 22%/3% 1 10 4 7m5spc-hpa Deployment/nginx 22%/3% 1 10 8 7m21spc-hpa Deployment/nginx 6%/3% 1 10 8 7m51spc-hpa Deployment/nginx 0%/3% 1 10 8 9m6spc-hpa Deployment/nginx 0%/3% 1 10 8 13mpc-hpa Deployment/nginx 0%/3% 1 10 1 14m deployment变化 [root@k8s-master01 ~] kubectl get deployment -n dev -wNAME READY UP-TO-DATE AVAILABLE AGEnginx 1/1 1 1 11mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 1 1 13mnginx 1/4 4 1 13mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 4 1 14mnginx 1/8 8 1 14mnginx 2/8 8 2 14mnginx 3/8 8 3 14mnginx 4/8 8 4 14mnginx 5/8 8 5 14mnginx 6/8 8 6 14mnginx 7/8 8 7 14mnginx 8/8 8 8 15mnginx 8/1 8 8 20mnginx 8/1 8 8 20mnginx 1/1 1 1 20m pod变化 [root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEnginx-7df9756ccc-bh8dr 1/1 Running 0 11mnginx-7df9756ccc-cpgrv 0/1 Pending 0 0snginx-7df9756ccc-8zhwk 0/1 Pending 0 0snginx-7df9756ccc-rr9bn 0/1 Pending 0 0snginx-7df9756ccc-cpgrv 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 0/1 ContainerCreating 0 0snginx-7df9756ccc-rr9bn 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 Pending 0 0snginx-7df9756ccc-sl9c6 0/1 Pending 0 0snginx-7df9756ccc-fgst7 0/1 Pending 0 0snginx-7df9756ccc-g56qb 0/1 ContainerCreating 0 0snginx-7df9756ccc-m9gsj 0/1 ContainerCreating 0 0snginx-7df9756ccc-sl9c6 0/1 ContainerCreating 0 0snginx-7df9756ccc-fgst7 0/1 ContainerCreating 0 0snginx-7df9756ccc-8zhwk 1/1 Running 0 19snginx-7df9756ccc-rr9bn 1/1 Running 0 30snginx-7df9756ccc-m9gsj 1/1 Running 0 21snginx-7df9756ccc-cpgrv 1/1 Running 0 47snginx-7df9756ccc-sl9c6 1/1 Running 0 33snginx-7df9756ccc-g56qb 1/1 Running 0 48snginx-7df9756ccc-fgst7 1/1 Running 0 66snginx-7df9756ccc-fgst7 1/1 Terminating 0 6m50snginx-7df9756ccc-8zhwk 1/1 Terminating 0 7m5snginx-7df9756ccc-cpgrv 1/1 Terminating 0 7m5snginx-7df9756ccc-g56qb 1/1 Terminating 0 6m50snginx-7df9756ccc-rr9bn 1/1 Terminating 0 7m5snginx-7df9756ccc-m9gsj 1/1 Terminating 0 6m50snginx-7df9756ccc-sl9c6 1/1 Terminating 0 6m50s DaemonSet 简称DS,ds可以保证在集群中的每一台节点(或指定节点)上都运行一个副本,一般适用于日志收集、节点监控等场景;也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。 DaemonSet控制器的特点: 每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上 当节点从集群中移除时,Pod 也就被垃圾回收了 配置模板 apiVersion: apps/v1 版本号kind: DaemonSet 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: daemonsetspec: 详情描述revisionHistoryLimit: 3 保留历史版本updateStrategy: 更新策略type: RollingUpdate 滚动更新策略rollingUpdate: 滚动更新maxUnavailable: 1 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数selector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: nginx-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [nginx-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80 1、创建ds 创建pc-daemonset.yaml,内容如下: apiVersion: apps/v1kind: DaemonSet metadata:name: pc-daemonsetnamespace: devspec: selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.17.1 运行 创建daemonset[root@k8s-master01 ~] kubectl create -f pc-daemonset.yamldaemonset.apps/pc-daemonset created 查看daemonset[root@k8s-master01 ~] kubectl get ds -n dev -o wideNAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES pc-daemonset 2 2 2 2 2 24s nginx nginx:1.17.1 查看pod,发现在每个Node上都运行一个pod[root@k8s-master01 ~] kubectl get pods -n dev -o wideNAME READY STATUS RESTARTS AGE IP NODE pc-daemonset-9bck8 1/1 Running 0 37s 10.244.1.43 node1 pc-daemonset-k224w 1/1 Running 0 37s 10.244.2.74 node2 2、删除daemonset [root@k8s-master01 ~] kubectl delete -f pc-daemonset.yamldaemonset.apps "pc-daemonset" deleted Job 主要用于负责批量处理一次性(每个任务仅运行一次就结束)任务。当然,你也可以运行多次,配置好即可,Job特点如下: 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量 当成功结束的pod达到指定的数量时,Job将完成执行 配置模板 apiVersion: batch/v1 版本号kind: Job 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: jobspec: 详情描述completions: 1 指定job需要成功运行Pods的次数。默认值: 1parallelism: 1 指定job在任一时刻应该并发运行Pods的数量。默认值: 1activeDeadlineSeconds: 30 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。backoffLimit: 6 指定job失败后进行重试的次数。默认是6manualSelector: true 是否可以使用selector选择器选择pod,默认是falseselector: 选择器,通过它指定该控制器管理哪些podmatchLabels: Labels匹配规则app: counter-podmatchExpressions: Expressions匹配规则- {key: app, operator: In, values: [counter-pod]}template: 模板,当副本数量不足时,会根据下面的模板创建pod副本metadata:labels:app: counter-podspec:restartPolicy: Never 重启策略只能设置为Never或者OnFailurecontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"] 关于重启策略设置的说明:(这里只能设置为Never或者OnFailure) 如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变 如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always 1、创建一个job 创建pc-job.yaml,内容如下: apiVersion: batch/v1kind: Job metadata:name: pc-jobnamespace: devspec:manualSelector: trueselector:matchLabels:app: counter-podtemplate:metadata:labels:app: counter-podspec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 创建 创建job[root@k8s-master01 ~] kubectl create -f pc-job.yamljob.batch/pc-job created 查看job[root@k8s-master01 ~] kubectl get job -n dev -o wide -wNAME COMPLETIONS DURATION AGE CONTAINERS IMAGES SELECTORpc-job 0/1 21s 21s counter busybox:1.30 app=counter-podpc-job 1/1 31s 79s counter busybox:1.30 app=counter-pod 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-rxg96 1/1 Running 0 29spc-job-rxg96 0/1 Completed 0 33s 接下来,调整下pod运行的总数量和并行数量 即:在spec下设置下面两个选项 completions: 6 指定job需要成功运行Pods的次数为6 parallelism: 3 指定job并发运行Pods的数量为3 然后重新运行job,观察效果,此时会发现,job会每次运行3个pod,总共执行了6个pod[root@k8s-master01 ~] kubectl get pods -n dev -wNAME READY STATUS RESTARTS AGEpc-job-684ft 1/1 Running 0 5spc-job-jhj49 1/1 Running 0 5spc-job-pfcvh 1/1 Running 0 5spc-job-684ft 0/1 Completed 0 11spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 Pending 0 0spc-job-v7rhr 0/1 ContainerCreating 0 0spc-job-jhj49 0/1 Completed 0 11spc-job-fhwf7 0/1 Pending 0 0spc-job-fhwf7 0/1 Pending 0 0spc-job-pfcvh 0/1 Completed 0 11spc-job-5vg2j 0/1 Pending 0 0spc-job-fhwf7 0/1 ContainerCreating 0 0spc-job-5vg2j 0/1 Pending 0 0spc-job-5vg2j 0/1 ContainerCreating 0 0spc-job-fhwf7 1/1 Running 0 2spc-job-v7rhr 1/1 Running 0 2spc-job-5vg2j 1/1 Running 0 3spc-job-fhwf7 0/1 Completed 0 12spc-job-v7rhr 0/1 Completed 0 12spc-job-5vg2j 0/1 Completed 0 12s 2、删除 删除jobkubectl delete -f pc-job.yaml CronJob 简称为CJ,CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。可以理解为定时任务 配置模板 apiVersion: batch/v1beta1 版本号kind: CronJob 类型 metadata: 元数据name: rs名称 namespace: 所属命名空间 labels: 标签controller: cronjobspec: 详情描述schedule: cron格式的作业调度运行时间点,用于控制任务在什么时间执行concurrencyPolicy: 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业failedJobHistoryLimit: 为失败的任务执行保留的历史记录数,默认为1successfulJobHistoryLimit: 为成功的任务执行保留的历史记录数,默认为3startingDeadlineSeconds: 启动作业错误的超时时长jobTemplate: job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义metadata:spec:completions: 1parallelism: 1activeDeadlineSeconds: 30backoffLimit: 6manualSelector: trueselector:matchLabels:app: counter-podmatchExpressions: 规则- {key: app, operator: In, values: [counter-pod]}template:metadata:labels:app: counter-podspec:restartPolicy: Never containers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"] cron表达式写法 需要重点解释的几个选项:schedule: cron表达式,用于指定任务的执行时间/1 <分钟> <小时> <日> <月份> <星期>分钟 值从 0 到 59.小时 值从 0 到 23.日 值从 1 到 31.月 值从 1 到 12.星期 值从 0 到 6, 0 代表星期日多个时间可以用逗号隔开; 范围可以用连字符给出;可以作为通配符; /表示每... 例如1 // 每个小时的第一分钟执行/1 // 每分钟都执行concurrencyPolicy:Allow: 允许Jobs并发运行(默认)Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行Replace: 替换,取消当前正在运行的作业并用新作业替换它 1、创建cronJob 创建pc-cronjob.yaml,内容如下: apiVersion: batch/v1beta1kind: CronJobmetadata:name: pc-cronjobnamespace: devlabels:controller: cronjobspec:schedule: "/1 " 每分钟执行一次jobTemplate:metadata:spec:template:spec:restartPolicy: Nevercontainers:- name: counterimage: busybox:1.30command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 3;done"] 运行 创建cronjob[root@k8s-master01 ~] kubectl create -f pc-cronjob.yamlcronjob.batch/pc-cronjob created 查看cronjob[root@k8s-master01 ~] kubectl get cronjobs -n devNAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGEpc-cronjob /1 False 0 <none> 6s 查看job[root@k8s-master01 ~] kubectl get jobs -n devNAME COMPLETIONS DURATION AGEpc-cronjob-1592587800 1/1 28s 3m26spc-cronjob-1592587860 1/1 28s 2m26spc-cronjob-1592587920 1/1 28s 86s 查看pod[root@k8s-master01 ~] kubectl get pods -n devpc-cronjob-1592587800-x4tsm 0/1 Completed 0 2m24spc-cronjob-1592587860-r5gv4 0/1 Completed 0 84spc-cronjob-1592587920-9dxxq 1/1 Running 0 24s 2、删除cronjob kubectl delete -f pc-cronjob.yaml pod调度 什么是调度 默认情况下,一个pod在哪个node节点上运行,是通过scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的; 调度规则 但是在实际使用中,我们想控制某些pod定向到达某个节点上,应该怎么做呢?其实k8s提供了四类调度规则 调度方式 描述 自动调度 通过scheduler组件采用相应的算法计算得出运行在哪个节点上 定向调度 运行到指定的node节点上,通过NodeName、NodeSelector实现 亲和性调度 跟谁关系好就调度到哪个节点上 1、nodeAffinity :节点亲和性,调度到关系好的节点上 2、podAffinity:pod亲和性,调度到关系好的pod所在的节点上 3、PodAntAffinity:pod反清河行,调度到关系差的那个pod所在的节点上 污点(容忍)调度 污点是站在node的角度上的,比如果nodeA有一个污点,大家都别来,此时nodeA会拒绝master调度过来的pod 定向调度 指的是利用在pod上声明nodeName或nodeSelector的方式将pod调度到指定的pod节点上,因为这种定向调度是强制性的,所以如果node节点不存在的话,也会向上面进行调度,只不过pod会运行失败; 1、定向调度-> nodeName nodeName 是将pod强制调度到指定名称的node节点上,这种方式跳过了scheduler的调度逻辑,直接将pod调度到指定名称的节点上,配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeName: node1 调度到node1节点上 2、定向调度 -> NodeSelector NodeSelector是将pod调度到添加了指定label标签的node节点上,它是通过k8s的label-selector机制实现的,也就是说,在创建pod之前,会由scheduler用matchNodeSelecto调度策略进行label标签的匹配,找出目标node,然后在将pod调度到目标node; 要实验NodeSelector,首先得给node节点加上label标签 kubectl label nodes node1 nodetag=node1 配置文件内容如下 apiVersion: v1 版本号kind: Pod 资源类型metadata: name: pod-namenamespace: devspec: containers: - image: nginx:1.17.1name: nginx-containernodeSelector: nodetag: node1 调度到具有nodetag=node1标签的节点上 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_27184497/article/details/121765387。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-09-29 09:08:28
422
转载
转载文章
...的一种基于键值的状态管理API,它允许开发者维护每个key的最新状态,并在每次接收到新的数据时更新对应key的状态值。在广告点击综合案例中,可以利用updateStateByKey来实时过滤黑名单用户、跟踪用户的点击次数以及按地区统计广告点击排名等复杂状态信息,以满足业务需求中的动态过滤有效用户点击行为、在线计算广告点击流量和区域分布分析等功能。 Structured Streaming , 虽然文章未直接提到Structured Streaming,但在Spark 2.x及更高版本中,Structured Streaming是Spark Streaming框架的一个重要演进方向,提供了更高级别的抽象和SQL-like API,使得实时流处理更加简单和直观。若在类似场景下采用Structured Streaming,可以通过定义DataFrame/Dataset查询语句的方式处理广告点击流数据,实现诸如黑名单过滤、流量统计、热点广告分析等任务。 MySQL , MySQL是一个开源的关系型数据库管理系统,在此案例中扮演着持久化存储系统的角色。经过Spark Streaming实时处理后的结果数据,如广告点击流量统计结果、热门广告排行榜等,会被写入到MySQL中以便于后续查询展示和报表生成,同时也便于其他系统和服务实时获取最新的广告效果数据。
2023-02-14 19:16:35
297
转载
转载文章
...ovs常用命令 控制管理类 1.查看网桥和端口 ovs-vsctl show 1 2.创建一个网桥 ovs-vsctl add-br br0 ovs-vsctl set bridge br0 datapath_type=netdev 1 2 3.添加/删除一个端口 for system interfaces ovs-vsctl add-port br0 eth1 ovs-vsctl del-port br0 eth1 for DPDK ovs-vsctl add-port br0 dpdk1 -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 for DPDK bonds ovs-vsctl add-bond br0 dpdkbond0 dpdk1 dpdk2 \ -- set interface dpdk1 type=dpdk options:dpdk-devargs=0000:01:00.0 \ -- set interface dpdk2 type=dpdk options:dpdk-devargs=0000:02:00.0 1 2 3 4 5 6 7 8 9 4.设置/清除网桥的openflow协议版本 ovs-vsctl set bridge br0 protocols=OpenFlow13 ovs-vsctl clear bridge br0 protocols 1 2 5.查看某网桥当前流表 ovs-ofctl dump-flows br0 ovs-ofctl -O OpenFlow13 dump-flows br0 ovs-appctl bridge/dump-flows br0 1 2 3 6.设置/删除控制器 ovs-vsctl set-controller br0 tcp:1.2.3.4:6633 ovs-vsctl del-controller br0 1 2 7.查看控制器列表 ovs-vsctl list controller 1 8.设置/删除被动连接控制器 ovs-vsctl set-manager tcp:1.2.3.4:6640 ovs-vsctl get-manager ovs-vsctl del-manager 1 2 3 9.设置/移除可选选项 ovs-vsctl set Interface eth0 options:link_speed=1G ovs-vsctl remove Interface eth0 options link_speed 1 2 10.设置fail模式,支持standalone或者secure standalone(default):清除所有控制器下发的流表,ovs自己接管 secure:按照原来流表继续转发 ovs-vsctl del-fail-mode br0 ovs-vsctl set-fail-mode br0 secure ovs-vsctl get-fail-mode br0 1 2 3 11.查看接口id等 ovs-appctl dpif/show 1 12.查看接口统计 ovs-ofctl dump-ports br0 1 流表类 流表操作 1.添加普通流表 ovs-ofctl add-flow br0 in_port=1,actions=output:2 1 2.删除所有流表 ovs-ofctl del-flows br0 1 3.按匹配项来删除流表 ovs-ofctl del-flows br0 "in_port=1" 1 匹配项 1.匹配vlan tag,范围为0-4095 ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan=777,actions=output:2 1 2.匹配vlan pcp,范围为0-7 ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan_pcp=7,actions=output:2 1 3.匹配源/目的MAC ovs-ofctl add-flow br0 in_port=1,dl_src=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 ovs-ofctl add-flow br0 in_port=1,dl_dst=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 1 2 4.匹配以太网类型,范围为0-65535 ovs-ofctl add-flow br0 in_port=1,dl_type=0x0806,actions=output:2 1 5.匹配源/目的IP 条件:指定dl_type=0x0800,或者ip/tcp ovs-ofctl add-flow br0 ip,in_port=1,nw_src=10.10.0.0/16,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.20.0.0/16,actions=output:2 1 2 6.匹配协议号,范围为0-255 条件:指定dl_type=0x0800或者ip ICMP ovs-ofctl add-flow br0 ip,in_port=1,nw_proto=1,actions=output:2 7.匹配IP ToS/DSCP,tos范围为0-255,DSCP范围为0-63 条件:指定dl_type=0x0800/0x86dd,并且ToS低2位会被忽略(DSCP值为ToS的高6位,并且低2位为预留位) ovs-ofctl add-flow br0 ip,in_port=1,nw_tos=68,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,ip_dscp=62,actions=output:2 8.匹配IP ecn位,范围为0-3 条件:指定dl_type=0x0800/0x86dd ovs-ofctl add-flow br0 ip,in_port=1,ip_ecn=2,actions=output:2 9.匹配IP TTL,范围为0-255 ovs-ofctl add-flow br0 ip,in_port=1,nw_ttl=128,actions=output:2 10.匹配tcp/udp,源/目的端口,范围为0-65535 匹配源tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_src=179/0xfff0,actions=output:2 匹配目的tcp端口179 ovs-ofctl add-flow br0 tcp,tcp_dst=179/0xfff0,actions=output:2 匹配源udp端口1234 ovs-ofctl add-flow br0 udp,udp_src=1234/0xfff0,actions=output:2 匹配目的udp端口1234 ovs-ofctl add-flow br0 udp,udp_dst=1234/0xfff0,actions=output:2 11.匹配tcp flags tcp flags=fin,syn,rst,psh,ack,urg,ece,cwr,ns ovs-ofctl add-flow br0 tcp,tcp_flags=ack,actions=output:2 12.匹配icmp code,范围为0-255 条件:指定icmp ovs-ofctl add-flow br0 icmp,icmp_code=2,actions=output:2 13.匹配vlan TCI TCI低12位为vlan id,高3位为priority,例如tci=0xf123则vlan_id为0x123和vlan_pcp=7 ovs-ofctl add-flow br0 in_port=1,vlan_tci=0xf123,actions=output:2 14.匹配mpls label 条件:指定dl_type=0x8847/0x8848 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=7,actions=output:2 15.匹配mpls tc,范围为0-7 条件:指定dl_type=0x8847/0x8848 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_tc=7,actions=output:2 1 16.匹配tunnel id,源/目的IP 匹配tunnel id ovs-ofctl add-flow br0 in_port=1,tun_id=0x7/0xf,actions=output:2 匹配tunnel源IP ovs-ofctl add-flow br0 in_port=1,tun_src=192.168.1.0/255.255.255.0,actions=output:2 匹配tunnel目的IP ovs-ofctl add-flow br0 in_port=1,tun_dst=192.168.1.0/255.255.255.0,actions=output:2 一些匹配项的速记符 速记符 匹配项 ip dl_type=0x800 ipv6 dl_type=0x86dd icmp dl_type=0x0800,nw_proto=1 icmp6 dl_type=0x86dd,nw_proto=58 tcp dl_type=0x0800,nw_proto=6 tcp6 dl_type=0x86dd,nw_proto=6 udp dl_type=0x0800,nw_proto=17 udp6 dl_type=0x86dd,nw_proto=17 sctp dl_type=0x0800,nw_proto=132 sctp6 dl_type=0x86dd,nw_proto=132 arp dl_type=0x0806 rarp dl_type=0x8035 mpls dl_type=0x8847 mplsm dl_type=0x8848 指令动作 1.动作为出接口 从指定接口转发出去 ovs-ofctl add-flow br0 in_port=1,actions=output:2 1 2.动作为指定group group id为已创建的group table ovs-ofctl add-flow br0 in_port=1,actions=group:666 1 3.动作为normal 转为L2/L3处理流程 ovs-ofctl add-flow br0 in_port=1,actions=normal 1 4.动作为flood 从所有物理接口转发出去,除了入接口和已关闭flooding的接口 ovs-ofctl add-flow br0 in_port=1,actions=flood 1 5.动作为all 从所有物理接口转发出去,除了入接口 ovs-ofctl add-flow br0 in_port=1,actions=all 1 6.动作为local 一般是转发给本地网桥 ovs-ofctl add-flow br0 in_port=1,actions=local 1 7.动作为in_port 从入接口转发回去 ovs-ofctl add-flow br0 in_port=1,actions=in_port 1 8.动作为controller 以packet-in消息上送给控制器 ovs-ofctl add-flow br0 in_port=1,actions=controller 1 9.动作为drop 丢弃数据包操作 ovs-ofctl add-flow br0 in_port=1,actions=drop 1 10.动作为mod_vlan_vid 修改报文的vlan id,该选项会使vlan_pcp置为0 ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:8,output:2 1 11.动作为mod_vlan_pcp 修改报文的vlan优先级,该选项会使vlan_id置为0 ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_pcp:7,output:2 1 12.动作为strip_vlan 剥掉报文内外层vlan tag ovs-ofctl add-flow br0 in_port=1,actions=strip_vlan,output:2 1 13.动作为push_vlan 在报文外层压入一层vlan tag,需要使用openflow1.1以上版本兼容 ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:2 1 ps: set field值为4096+vlan_id,并且vlan优先级为0,即4096-8191,对应的vlan_id为0-4095 14.动作为push_mpls 修改报文的ethertype,并且压入一个MPLS LSE ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,set_field:10-\>mpls_label,output:2 1 15.动作为pop_mpls 剥掉最外层mpls标签,并且修改ethertype为非mpls类型 ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=20,actions=pop_mpls:0x0800,output:2 1 16.动作为修改源/目的MAC,修改源/目的IP 修改源MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_src:00:00:00:00:00:01,output:2 修改目的MAC ovs-ofctl add-flow br0 in_port=1,actions=mod_dl_dst:00:00:00:00:00:01,output:2 修改源IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_src:192.168.1.1,output:2 修改目的IP ovs-ofctl add-flow br0 in_port=1,actions=mod_nw_dst:192.168.1.1,output:2 17.动作为修改TCP/UDP/SCTP源目的端口 修改TCP源端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_src:67,output:2 修改TCP目的端口 ovs-ofctl add-flow br0 tcp,in_port=1,actions=mod_tp_dst:68,output:2 修改UDP源端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_src:67,output:2 修改UDP目的端口 ovs-ofctl add-flow br0 udp,in_port=1,actions=mod_tp_dst:68,output:2 18.动作为mod_nw_tos 条件:指定dl_type=0x0800 修改ToS字段的高6位,范围为0-255,值必须为4的倍数,并且不会去修改ToS低2位ecn值 ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_tos:68,output:2 1 19.动作为mod_nw_ecn 条件:指定dl_type=0x0800,需要使用openflow1.1以上版本兼容 修改ToS字段的低2位,范围为0-3,并且不会去修改ToS高6位的DSCP值 ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_ecn:2,output:2 1 20.动作为mod_nw_ttl 修改IP报文ttl值,需要使用openflow1.1以上版本兼容 ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=mod_nw_ttl:6,output:2 1 21.动作为dec_ttl 对IP报文进行ttl自减操作 ovs-ofctl add-flow br0 in_port=1,actions=dec_ttl,output:2 1 22.动作为set_mpls_label 对报文最外层mpls标签进行修改,范围为20bit值 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_label:666,output:2 1 23.动作为set_mpls_tc 对报文最外层mpls tc进行修改,范围为0-7 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_tc:7,output:2 1 24.动作为set_mpls_ttl 对报文最外层mpls ttl进行修改,范围为0-255 ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_ttl:255,output:2 1 25.动作为dec_mpls_ttl 对报文最外层mpls ttl进行自减操作 ovs-ofctl add-flow br0 in_port=1,actions=dec_mpls_ttl,output:2 1 26.动作为move NXM字段 使用move参数对NXM字段进行操作 将报文源MAC复制到目的MAC字段,并且将源MAC改为00:00:00:00:00:01 ovs-ofctl add-flow br0 in_port=1,actions=move:NXM_OF_ETH_SRC[]-\>NXM_OF_ETH_DST[],mod_dl_src:00:00:00:00:00:01,output:2 1 2 ps: 常用NXM字段参照表 NXM字段 报文字段 NXM_OF_ETH_SRC 源MAC NXM_OF_ETH_DST 目的MAC NXM_OF_ETH_TYPE 以太网类型 NXM_OF_VLAN_TCI vid NXM_OF_IP_PROTO IP协议号 NXM_OF_IP_TOS IP ToS值 NXM_NX_IP_ECN IP ToS ECN NXM_OF_IP_SRC 源IP NXM_OF_IP_DST 目的IP NXM_OF_TCP_SRC TCP源端口 NXM_OF_TCP_DST TCP目的端口 NXM_OF_UDP_SRC UDP源端口 NXM_OF_UDP_DST UDP目的端口 NXM_OF_SCTP_SRC SCTP源端口 NXM_OF_SCTP_DST SCTP目的端口 27.动作为load NXM字段 使用load参数对NXM字段进行赋值操作 push mpls label,并且把10(0xa)赋值给mpls label ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,load:0xa-\>OXM_OF_MPLS_LABEL[],output:2 对目的MAC进行赋值 ovs-ofctl add-flow br0 in_port=1,actions=load:0x001122334455-\>OXM_OF_ETH_DST[],output:2 1 2 3 4 28.动作为pop_vlan 弹出报文最外层vlan tag ovs-ofctl add-flow br0 in_port=1,dl_type=0x8100,dl_vlan=777,actions=pop_vlan,output:2 1 meter表 常用操作 由于meter表是openflow1.3版本以后才支持,所以所有命令需要指定OpenFlow1.3版本以上 ps: 在openvswitch-v2.8之前的版本中,还不支持meter 在v2.8版本之后已经实现,要正常使用的话,需要注意的是datapath类型要指定为netdev,band type暂时只支持drop,还不支持DSCP REMARK 1.查看当前设备对meter的支持 ovs-ofctl -O OpenFlow13 meter-features br0 2.查看meter表 ovs-ofctl -O OpenFlow13 dump-meters br0 3.查看meter统计 ovs-ofctl -O OpenFlow13 meter-stats br0 4.创建meter表 限速类型以kbps(kilobits per second)计算,超过20kb/s则丢弃 ovs-ofctl -O OpenFlow13 add-meter br0 meter=1,kbps,band=type=drop,rate=20 同上,增加burst size参数 ovs-ofctl -O OpenFlow13 add-meter br0 meter=2,kbps,band=type=drop,rate=20,burst_size=256 同上,增加stats参数,对meter进行计数统计 ovs-ofctl -O OpenFlow13 add-meter br0 meter=3,kbps,stats,band=type=drop,rate=20,burst_size=256 限速类型以pktps(packets per second)计算,超过1000pkt/s则丢弃 ovs-ofctl -O OpenFlow13 add-meter br0 meter=4,pktps,band=type=drop,rate=1000 5.删除meter表 删除全部meter表 ovs-ofctl -O OpenFlow13 del-meters br0 删除meter id=1 ovs-ofctl -O OpenFlow13 del-meter br0 meter=1 6.创建流表 ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=meter:1,output:2 group表 由于group表是openflow1.1版本以后才支持,所以所有命令需要指定OpenFlow1.1版本以上 常用操作 group table支持4种类型 all:所有buckets都执行一遍 select: 每次选择其中一个bucket执行,常用于负载均衡应用 ff(FAST FAILOVER):快速故障修复,用于检测解决接口等故障 indirect:间接执行,类似于一个函数方法,被另一个group来调用 1.查看当前设备对group的支持 ovs-ofctl -O OpenFlow13 dump-group-features br0 2.查看group表 ovs-ofctl -O OpenFlow13 dump-groups br0 3.创建group表 类型为all ovs-ofctl -O OpenFlow13 add-group br0 group_id=1,type=all,bucket=output:1,bucket=output:2,bucket=output:3 类型为select ovs-ofctl -O OpenFlow13 add-group br0 group_id=2,type=select,bucket=output:1,bucket=output:2,bucket=output:3 类型为select,指定hash方法(5元组,OpenFlow1.5+) ovs-ofctl -O OpenFlow15 add-group br0 group_id=3,type=select,selection_method=hash,fields=ip_src,bucket=output:2,bucket=output:3 4.删除group表 ovs-ofctl -O OpenFlow13 del-groups br0 group_id=2 5.创建流表 ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=group:2 goto table配置 数据流先从table0开始匹配,如actions有goto_table,再进行后续table的匹配,实现多级流水线,如需使用goto table,则创建流表时,指定table id,范围为0-255,不指定则默认为table0 1.在table0中添加一条流表条目 ovs-ofctl add-flow br0 table=0,in_port=1,actions=goto_table=1 2.在table1中添加一条流表条目 ovs-ofctl add-flow br0 table=1,ip,nw_dst=10.10.0.0/16,actions=output:2 tunnel配置 如需配置tunnel,必需确保当前系统对各tunnel的remote ip网络可达 gre 1.创建一个gre接口,并且指定端口id=1001 ovs-vsctl add-port br0 gre1 -- set Interface gre1 type=gre options:remote_ip=1.1.1.1 ofport_request=1001 2.可选选项 将tos或者ttl在隧道上继承,并将tunnel id设置成123 ovs-vsctl set Interface gre1 options:tos=inherit options:ttl=inherit options:key=123 3.创建关于gre流表 封装gre转发 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:1001 解封gre转发 ovs-ofctl add-flow br0 in_port=1001,actions=output:1 vxlan 1.创建一个vxlan接口,并且指定端口id=2001 ovs-vsctl add-port br0 vxlan1 -- set Interface vxlan1 type=vxlan options:remote_ip=1.1.1.1 ofport_request=2001 2.可选选项 将tos或者ttl在隧道上继承,将vni设置成123,UDP目的端为设置成8472(默认为4789) ovs-vsctl set Interface vxlan1 options:tos=inherit options:ttl=inherit options:key=123 options:dst_port=8472 3.创建关于vxlan流表 封装vxlan转发 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.10.0.0/16,actions=output:2001 解封vxlan转发 ovs-ofctl add-flow br0 in_port=2001,actions=output:1 sflow配置 1.对网桥br0进行sflow监控 agent: 与collector通信所在的网口名,通常为管理口 target: collector监听的IP地址和端口,端口默认为6343 header: sFlow在采样时截取报文头的长度 polling: 采样时间间隔,单位为秒 ovs-vsctl -- --id=@sflow create sflow agent=eth0 target=\"10.0.0.1:6343\" header=128 sampling=64 polling=10 -- set bridge br0 sflow=@sflow 2.查看创建的sflow ovs-vsctl list sflow 3.删除对应的网桥sflow配置,参数为sFlow UUID ovs-vsctl remove bridge br0 sflow 7b9b962e-fe09-407c-b224-5d37d9c1f2b3 4.删除网桥下所有sflow配置 ovs-vsctl -- clear bridge br0 sflow 1 QoS配置 ingress policing 1.配置ingress policing,对接口eth0入流限速10Mbps ovs-vsctl set interface eth0 ingress_policing_rate=10000 ovs-vsctl set interface eth0 ingress_policing_burst=8000 2.清除相应接口的ingress policer配置 ovs-vsctl set interface eth0 ingress_policing_rate=0 ovs-vsctl set interface eth0 ingress_policing_burst=0 3.查看接口ingress policer配置 ovs-vsctl list interface eth0 4.查看网桥支持的Qos类型 ovs-appctl qos/show-types br0 端口镜像配置 1.配置eth0收到/发送的数据包镜像到eth1 ovs-vsctl -- set bridge br0 mirrors=@m \ -- --id=@eth0 get port eth0 \ -- --id=@eth1 get port eth1 \ -- --id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1 2.删除端口镜像配置 ovs-vsctl -- --id=@m get mirror mymirror -- remove bridge br0 mirrors @m 3.清除网桥下所有端口镜像配置 ovs-vsctl clear bridge br0 mirrors 4.查看端口镜像配置 ovs-vsctl get bridge br0 mirrors Open vSwitch中有多个命令,分别有不同的作用,大致如下: ovs-vsctl用于控制ovs db ovs-ofctl用于管理OpenFlow switch 的 flow ovs-dpctl用于管理ovs的datapath ovs-appctl用于查询和管理ovs daemon 转载于:https://www.cnblogs.com/liuhongru/p/10336849.html 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_30876945/article/details/99916308。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-06-08 17:13:19
294
转载
转载文章
...历软件泡沫的投资家、管理者在对待员工的薪水上更为谨慎,但对开发者而言心理上却产生比较大的落差,在大环境如此的情况下,处于弱势的开发群体需要学会如何去适应环境,调整心态。 程序员占据大壁江山,升任技术总监者凤毛麟角 从本次的调查数据来看,程序员在所有调查者中占据主流,人数为一半还多,高级程序员也占了20%,这也是为什么开发者薪资普遍不高的主要原因之一。曾经业界大为盛行的国内缺乏高层次的软件人才的说法,这里似乎可以提供实在而有力的数据支持(见表3、表4)。 另外,从本次调查还得到了一个趋势:在做了3-5年的程序开发工作后,开始产生一定的人员分流现象。从有一定技术能力的程序员开始,到根据自己兴趣与爱好的二次择业,有相当部分的人员脱离编码一线,开始跨入技术主管、项目经理、技术支持、市场推广等角色。 不满者过半,普遍认为薪水太低 调查显示只有4%的人对薪水比较满意,近64%的人认为自己的薪水与社会同等能力开发人员相比偏低,这可以看出软件泡沫对开发人员造成的心理落差依然存在。人们普遍认为,软件业比较浮燥,所处其中的人也比较浮燥,但现在软件产业的发展越来越趋于理性和平和,只有先调整好自己的心态,平和地从基本功练起,薪水的价值才可能越来越得到不断提升。 软件开发,让女性走开 表5数据表明,开发者世界是一块绝对属于男性的天地,被调查者中有97%的人员属于男性。记者在采访中不止一次地发现,在软件公司中工作的女性很少,而从事一线编码工作的女性则是少之更少。一方面,软件开发这种技术创新与高挑战性、高压力的工作,男性更易于取得成果。另一方面,也有一部分中小企业对女性程序员不重视,甚至同工不同酬,也让一些希望就职此行业的女性永远地离开了这块阵地。 北京、上海、深圳、杭州成为程序员的最爱 地域对软件人员的薪资有很大的影响。北京以其政治、文化的优势集中了近19%的软件开发者,上海、深圳各占13%、10%,而杭州,以其良好的自然环境、人文环境及政府环境也吸引了5%的软件人才(见表6)。数据表明,拥有高校资源的城市先天性地占据着开发人才的绝对优势。而且,各项调查数据显示,地域也已不再是限制开发者流动的主要因素,尤其对于技术高手,他们几乎可以自由地在各大城市间来来往往。 情人虽好,糟糠之妻难下堂 哪些人在投资it企业,被调查者所在公司的规模如何?根据采访,几乎绝大多数的被调查者都将外企列在了第一选择,青睐之情溢于言表,但毕竟高高的门坎以及各种复杂因素,致使这些意愿大部分都难以实现。反而是那些遭到诸多抱怨的民营企业,尤其是占据31%的最高市场份额、员工数不足50人、管理不规范的中小软件公司,容纳了52%的开发者队伍。 c/c++、java成为翘楚,c实力强劲 调查显示,c/c++、java已是中国开发者的最爱,delphi依然延续着它的传奇之路,而c表现出了强大的后劲,相信这个微软公司推崇备至的开发利器在未来几年会如vb一样赢得开发者的信赖。 人气最旺的2大领域——企业信息化、通信 企业信息化、通信、通用软件开发、系统集成四大领域集中了目前开发者的大多数。加入wto之后,中国企业要与世界接轨,e化是必然的趋势,况且通信这个新兴行业以其门槛高、薪水高也吸引了许多开发者。企业信息化作为传统行业向网络化迈进的必然过程,容纳着很多软件人。另外,从市场角度看,移动、游戏开发、信息全三大热点领域对开发者也同样有极强诱惑力。 本科、计算机专业、部属院校大学毕业者成为中流砥柱 软件开发,并非只有计算机专业的人才能胜任,调查显示,有近40%的开发者是从其它相关或无关专业转行而来,但不可否认的是,占据60%者仍然为科班出身者。另外,尽管从来就崇尚高中毕业生就能成为软件天才,但这样的神话毕竟只是少数,支撑中国软件业的仍然是大学教育程度以上者。参与调查者中86%具有大专以上学历,另有8%的人具有硕士学历,数据表明中国开发者的整体教育水平较高。 综合实力的三大法宝:阅历、技术与沟通 59%的开发者从业期间做过的项目不超过5个,61%的人沟通能力较差,而近76%的开发者对自己比较自信,认为自己能力不弱于公司其它人员甚至更强。根据调查,在影响软件人薪资的因素中,阅历、技术强弱是决定性因素。另外,信息化时代普遍重视团队与项目整体实力,沟通能力成为影响程序员个人发展的一个重要因素。 软件人主体正处青春期 “程序员是吃青春饭的”,这个论断在本次调查中从另外一个角度得到验证。58%的软件开发者年龄不到25岁,48%的人在本领域工作时间不到3年,这些软件生力军未来5年必将成为引导中国软件发展潮流的主力军(见表18、表19)。另外,根据调查与采访,年龄在35岁左右的第二代软件人,现在已经成长为企业或项目的管理者,在各大软件公司担当着成熟、理性、有主见的软件开发带头人的角色。 待遇与福利走向正规化 有63%的公司会根据员工表现主动加薪(见表20),近80%的公司会为员工提供基本福利,如养老、医疗保险、住房补助、午餐补助等(见表21)。培训作为提升开发人员专业技能和实力的直接手段,越来越得到更多公司的重视。根据调查,项目奖金和固定假期基本成为以项目方式运作的公司的固定法宝,以鼓励和保障员工的士气和工作积极性。越来越多的中国软件企业,开始迈向规范化管理之路。 技术与眼光是决定薪水的至关要素 绝大部分被调查者都认为技术能力是决定薪资的最关键因素。但在采访过程中,却有更多的技术总监甚至公司总经理一级,认为短期内决定一个开发者薪水的因素中技术能力确实非常关键,但从长期来看,能对开发者的薪水带来长期且持久影响的,却不只是技术能力,更多的则是他本人对业界的了解度,即眼光是否开阔。这是一个很重要的信号,如果只在技术点上打转的人,除非是技术天才型,决大多数必须从综合能力等各方面来加强,而绝非技术这一点。可以说,在加强自身技术实力的前提下,开阔的视野、一定的沟通能力、自我管理与团队管理能力都对个人的发展起到至关重要的作用。(见表22) 现状解析:五维度立体定位开发者的薪资水平 结合以上调查结果以及本刊记者的深入采访,从宏观角度来看,有五个要素立体性地将软件人定位在了一定的薪资水平上。 这五个要素分别是:眼光技术、角色定位、公司性质、行业领域、地域因素。除第一、二要素是以个体原因占主体外,其他三个关键要素都取决于社会、产业、企业或公司本身的发展情况,但这些要素也不是一成不变的,在一定程度上,都是双向选择。 眼光技术是关键 一级:眼光与阅历 二级:核心技术 三级:专业与沟通 眼光开阔者得高薪 被采访者:王永刚 个人背景:软件公司cto 对于“决定薪资的最关键因素是什么”这个问题,王永刚用“是否适合职位”来回答,这一点与很多认为技术能力强就可以拿高薪的观点很不一样。他认为,多数职位分工不同,即便技术能力强但不适合职位,一样拿不到理想的薪水。他们公司在给员工定职定薪时,会与权威的咨询公司合作,从分析职位工作职责,到该职位所要求的人员素质,再到应聘员工对该职位的理解以及实际的工作情况,进行综合考虑。 专业与技术产生核心竞争力 被采访者:孙勇 个人背景:高级程序员,linux下c/c++开发 工作四年来,孙勇一直从事linux下使用c/c++进行的嵌入式开发,四年中跳过两次槽。跳槽前后的薪水变化很有意思,跳槽前月薪低年薪高,跳槽后月薪高但年薪却降了很多,原因是第一家公司项目奖金、年终分红很多,而第二家公司却没有其他方面的奖励机制。 孙勇自认为跳槽太过频繁,这样对自己技术能力的发展会产生较多的负面影响。在他看来,一个人薪资的高低终究取决于自己技术的核心竞争力,变动太大可能会造成技术上的不连续。所以孙勇说,未来五年内自己会沉浸于技术不考虑其它,目的只有一个,就是让自己更专业、更核心! 专家分析:眼光专业与核心竞争力是定位软件人层级的第一法码,其包含着很多的综合因素:专业背景、阅历、经验值、能力高下等等。趋势全球研发及资讯执行副总裁国屏认为,“技术很重要,但更重要的是市场和文化的配合。在个人的发展过程中,学习也会起到重要的作用。此外,还必须认同企业文化,具备技术、对工作、对解决问题的热情”。此外,学习能力和沟通能力也是专家们认为重要度很高的2个要素。当然,这其中,作为前提“最重要的还是兴趣,缘于自身对程序开发的热爱”,8848公司cto张研如是说。 角色大挪移 一级指标:cto、项目承包人 二级指标:架构师、部门主管/项目主管 三级指标:普通开发人员 从个人发展的角度和过程来看,这个指标应该是倒向。但从业界普遍的认识,无论是能力、阅历还是收入待遇,人们普遍对一级指标中的人员更多持赞赏态度。 被采访者:张齐生 个人背景:技术总监 起初,我只是在一家软件公司作java程序员,后来随着项目的进展以及工作时间的推移,自己的技术能力、项目管理能力也逐步加强,从最初的开发人员做到项目主管,2003年底的时候做到技术总监,工资范围也从最初的4000元到8000元,再到技术总监的万元,角色的改变确实带来了很多附加价值,当然,这个职位要求你带来的价值也会更多。 专家分析:出现这种工资结构是正常的。因为架构师、cto一般都是从普通开发人员过来的,具有深厚的业界开发经验和背景。联合信息集团移动应用开发部总经理熊军认为,开发人员必须“对自己能力的认识有一个准确的职业定位。认识自己,才能准确地职业定位,有了准确的职业定位,才能有短期、中期和长期的发展方向和动力。” 8848公司cto张研表示反对“学而优则士”、“不想当将军的士兵就不是好士兵”此类说法。同样,csdn网站、《程序员》杂志社总经理蒋涛也不建议所有程序员都向管理道路发展,因为相比之下,项目经理和cto必定具有一些独特的素质,比如沟通能力、项目管理能力,组织能力、计划能力以及产品和技术的眼光等,这些素质并不是每一个人都具备的。 公司对对碰 一级指标:外资、合资、民营大型it公司 二级指标:合资、中小软件公司 三级指标:国企、事业单位 采访中,有位叫王岩的资深开发人员一再强调,如果可能,一定要进外企。本次调查中,微软亚洲研究院,ibm研究院等外企几乎成了大部分开发人员所向往的圣地。 外企是我第一选择 被采访者:李文山 个人背景:技术支持 上海交大毕业的李文山,在校时就已经参与了很多社团活动,因此也见识了不少各种企业人员的做事风格与思想状态。外企大公司前沿的技术科研、严谨负责的处事态度都给他留下了深刻的印象。当然,丰富的培训、优厚的待遇、放心的福利也是必须考虑的因素。用他的话说,“身边全是一级的牛人,自己的发展自然就有了保障”。 中小软件企业机会多 被采访者:刘洋 个人背景:项目经理+程序员 天天加班加点,见到刘洋时他一脸的菜色,但心情不错。毕业不到一年,他就凭技术能力与管理能力当上了项目经理。虽然下面员工流动率高,但刘洋的薪水却是老板亲自钦点,比起毕业的同班同学绰绰有余。从项目最初的客户谈判、到中间执行,再到最后的交工,刘洋什么都做过,因此也锻炼得几乎成了全能手。对于未来,他希望公司业务做大后,能再规范一些,当然,随着公司的成长,自己上升的空间也很大。 三企走遍 被采访者:阿蒙(vchome.net) 个人背景:6年,通信行业,珠海 我很幸运,毕业时就进了美资软件公司,从事系统软件的开发工作,主要应用c/c++、x86汇编、mips汇编、ddk、sdk等技术,年薪四万多。在这家外企工作两年后,技术与处事能力大有提高,但开始心生厌倦,总觉得外面的世界很精彩。后来有一家从事通信软件产品开发的公司,答应年薪翻倍,一年后可走上管理层,怦然心动后就去新公司报到了。一年后,如愿以偿地走上管理层,两年后,技术管理能力以及行业业务能力有了质的飞跃,也越来越发现这个行业有前途,于是与朋友开始策划开公司,资金融到后就轰轰烈烈地创业了。没日没干了一年,由于资金与市场的原因,公司over,只好灰溜溜地去一家香港合资公司继续打工,仍做管理层。 我的感觉是,外企有一整套规章制度,薪金制度也较为完善,工作考评有客观的数值:月工作计划与总结、季度工作考核、上司的总体评价等,这些考核都很详细,细到完成的代码量、文档数、提过什么建议等等。国内企业也有计划与考核,但更多的是主观态度,而对工作的效果与过程并不具体细化,人际关系、表达能力等往往起着很微妙的关键作用。当然国内企业也有很多优点,比如制度灵活。 专家点评:人才的争夺,一方面是卯足了劲准备抢占有利地势和环境的个人开发者,另一方面,企业间的人才争夺战越演越烈。在此情况下,为了吸引国内的高素质人才,不少外企纷纷在中国开设研究院,走“曲线救国”道路。根据一份猎头资料,摩托罗拉研发中心、松下电器中国研究开发公司、ibm中国研究中心、朗讯公司贝尔实验室、微软中国研究院都是猎取高级科研、管理人才的大头。外企与外企、外企与国企、国企与民企,这个三角关系,虽然在早几年优劣非常明显,但现在,这种差距正在明显缩小。具体适合哪个企业,围城内外其实也并不是三重天(见下页表23)。 热点行业易淘金 一级推荐:移动开发、游戏开发 二级推荐:安全领域、企业信息化 三级推荐:通用软件、系统平台、项目开发等 专家点评:出现这种趋势主要是由市场对软件人才的供求决定的,因为目前在移动和游戏领域开发人员确实比较少,所以相对而言,他们的薪资较高,这就是所谓的“奇货可居”。但是,目前市场在成长,这些新兴或热点领域的开发人员数量也在逐渐增加,当达到一个平衡点时,他们的工资也会随之下降,这主要由市场对人才的供求关系决定。不建议开发人员轻易放弃自己原有的开发领域花大量时间和精力投向自己不熟悉的领域。 所以,熊军认为:这两个行业方向的长线发展看好,也需要更多的开发人员,但是年轻人都要根据自己的兴趣爱好、思维模式、技术能力选择更适合自己的行业方向,而且也有很多更有潜力的方向,建议年轻人从长远考虑。 地域火拼 一级指标:北京、上海 二级指标:深圳、杭州、广州 三级指标:成都、武汉、大连等 绝大多数的软件从业人员集中在北京、上海、广州和深圳四大城市,其中尤以北京的人数最为集中,但在另一项相关的调查中,上海却是程序员最向往的城市。在本次收入调查中,北京、上海的工资较高。武汉稍低于成都。 地域不同,薪资有别 被采访者:青润 个人背景:5年,电信行业、软件企业服务 我本人在北京、上海、深圳、成都四地都曾工作过。我基本上这样认为,对于刚刚大学毕业的软件人员,工资情况是这样:成都1500-2000元/月,上海2000元/月,深圳2000-2500元/月,北京2000-2500元/月。工作几年后,以成都系数为1来计,上海和其他地方为1.3-1.5倍于成都的收入。差异主要也是因为生活成本造成的。 相比而言,北京具有王者气氛,有着俯瞰全国的实力和影响力。上海是经济驱动的城市。深圳对人的友好度最好,它的优点是有各种各样的新技术公司,缺点是缺乏大公司的支撑。好山好水的成都,虽起步了很多软件公司,但大都在出川后倒下了,或者只是长居四川,足少出户,感觉比较舒适和懒散。 安逸的成都竞争的北京 被采访者:夏桅 个人背景:。net开发人员 夏桅毕业之后就来到北京从事软件开发工作。但他时常怀念起成都的生活,那里的山,那里的水,还有怡然自得的成都人都给他留下了深刻的印象。 但夏桅还是不后悔。一方面,安逸的环境对自己发展不利,适度的竞争可以发掘自身的潜力。而且,眼界开阔了,薪水也高不少。当然,在北京的生活绝对说不上舒服,但机会多,可有多种选择,极大地改观了自己的现状。 一眼可以看到头的武汉,但我喜欢 被采访者:刘如宁 个人背景:大学教师、项目主管 在武汉工作了10多年,刘如宁感觉还是比较惬意。比收入,武汉可能还不如成都,更别提北京和上海,但武汉的生活成本比较低,几块钱就够一天的伙食了。在高校担当大学教师的刘如宁,科研任务不重,而且还有足够的时间去外面承接项目,用自己喜欢的软件开发技术赚取外快。“我不是一个特别喜欢接受挑战的人,这种做自己喜欢的事情、宁静而富裕的生活,我还是比较满足”,有房、有车,生活安定富足的刘如宁如是说。 专家点评:比“营利”,必须是一个闭环。有收入比较,还得有支出比较,两者对比后才是最终收获。在地域这个问题上,大城市,确实收入比较高,但相对的,生活成本也较高。 趋势全球研发及资讯执行副总裁梁国屏表示,趋势的薪资结构体系在全世界都是一样的,具体数值要根据各地的市场来调整。比如一个经理,他的等级可能是10,那么不论在中国、日本还是美国,他的等级都是10.但这个等级的薪水具体是多少,就要看当地的市场了,趋势会和当地的薪资调查单位合作,来确定系数,然后计算出具体的薪水。 除薪水外,地域的附加价值会更重要一些。第一,对于技术发展比较迅速的it业,在大城市,整体的环境和氛围相对会好一些,例如在北京和上海等地,几乎每天都会有技术论坛、开发者大会、大厂商的开发日、各领域大师的巡回讲座等。其次,作的机会也会比较多,因为集中了各种类型的公司和企业,总会找到适合你条件的合适职位和选择。第三,可以参与比较大的技术团体,形成独特的生活与社交圈。用8848公司cto张研的话来说,“如果周围都是高手,你不是高手也难”,所以地域对人影响最大的是提供了一个环境,其次才是机会和薪水。 对此,telelogic公司北方区总经理任群力建议说,“如果开发人员能够善于利用互联网,并有决心多学习,这种地域差异会得到弱化。” 我拿青春赌明天 在本次专题组织中,大部分被采访人都明确表示,自己会在软件业领域一直奋斗下去,因为从中得到了很多的快乐与激情。但明天是否一定会更好,这需要从两个角度去考虑:一是从个人角度讲,年轻的软件人一定要有个人职业的规划,而且这种规划要从自己特点或专长出发,与当前业界相适应。另外,更重要的是,个人发展到什么程度,还需要同整个软件大环境和社会环境挂钩。 个人职业要规划 现在广州做了4年delphi/c行业开发、年薪10万的王旋说,“工作后所得到的收获就是,学习和工作要有相对明确的目标,不能因为一时心动而去学习某一技术。在真正下决定之前,我通常会考虑更多因素,包括长期的发展、个人路线的规划、需要付出的代价、可能遇到的困难以及解决的办法等等,在决定后还会制定更加明确的计划,包括短期、中期和长期的,身边可以利用到的资源,以及每一个阶段是怎么过渡到更高阶段的计划。” 现在,越来越多的在职人员意识到,未来的职业细分市场中,只有在某一领域确实比较深入、具有专长和资源的人会得到企业的重视,浪里淘沙勇者胜。 中国软件业面临困境 中国的软件业发展目前面临两难境地。上至国家,下至各城市都给予了相当的政策优惠,但整体软件业的发展却一直雷声大,雨点小。对此,北航软件学院院长孙伟忧心忡忡,“很多人从心里看不起印度,但印度的软件业却有数家2万、3万员工规模的大企业,放眼中国,规模最大的东软集团、用友公司,真正的软件开发者也不过两、三千人,这种差别太巨大了,我们一定要好好思考,中国的软件业究竟出了什么问题?” 对此,很多专家认为,中国软件业已经面临一个新的转折点,随着信息化在各行各业的深入运用,软件业有机会深度专业化,由边缘而进入核心,从而形成以深度专业化为特征的核心竞争力。无论个人还是公司,我们都有幸在第一时间站在了软件业这块前沿阵地,但明天是否会更好,还有待于中国软件业的整体发展,在这颇为沉闷的时刻,我们期望“让暴风雨来得更猛烈些吧”! 参考资料:http://www.w-training.com/viewc.asp?id=23922 ====================================================== 在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/ 本篇文章为转载内容。原文链接:https://blog.csdn.net/javazhuanzai/article/details/7189396。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-24 09:01:26
286
转载
转载文章
...越来越多,那么公司的管理成本则会非常昂贵”。 实际上对于开源活动的支持也变成了一种招聘途径。“这真是一块吸引人才的磁铁,”Ambiel说,“这也是新员工所寻求的“。 她还提到,一些工程经理可能会对贡献开源而减损核心产品的开发的精力而感到担忧。她补充到,他们的理由有可能是这样的:“我只有有限的才华与时间,且我需要这些只做我认为可以处理且看到投资回报的事情。” 但她说,这是一种鼠目寸光的态度。支持开源社区并且作出贡献的员工,可以从中培养技能与增长才干。 云安全供应商 Sysdig 的首席技术官兼创始人 Loris Degionni 也赞同这一观点:“找到为开源做出贡献的员工无疑就找到一座金矿,”他说。 他认为,这些参与开源的员工更具备公司想拥有的竞争力并将一些功能融入至社区所支持的标准中。且在人才争夺战中,拥抱开源的公司也更受到开发人员的青睐。 “最后,开源项目是由你可能无法聘请的技术专家社区推动的”,他说,“当员工积极参与并于这些专家合作时,他们将能更好地深入这些顶级的实践,并将这些收获带回到你的组织之中。” “当原始数据来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多...所以你很快就会变成一个开源项目里独一无二变体的”自豪“用户和维护人员。”— Suzanne Ambiel,VMware 开源营销和战略总监 “但是这一切终究不会白费--开发人员不应该把空闲时间用在磨练他们的技能上,因为你的公司很快就会在他们的努力中看到好处。” Degionni认为,OSPO(开源计划办公室)可以帮助公司实现这些目标,以及帮助确定贡献的优先级并确保合作的进行。除此之外,他们也可以对公司内部开发应用程序方面的治理提供相关帮助。 “开源团队的成员也可以成为开源技术的伟大内部传播者,并充当组织与更广泛社区之间的桥梁。”他补充道。 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月调查中,近 53% 的拥有 OSPO的组织表示,由于拥有了OSPO,他们看到了更多创新,而近 43% 的组织表示,他们在外部开源项目的参与度上有所增加。 Part3更多OSPO的好处:商业优势 网络安全公司 ThreatX 的首席创新官 Tom Hickman 表示,为开源社区做出贡献,不仅有助于社区,还有助于为社区做出贡献的公司。 “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与”,他说,“这可以变成一个良性循环。” 此外,根据哈佛商学院的研究,为开源项目作出贡献的公司从使用开源的项目中获得的生产价值,是不参与开源项目公司的两倍。 Cloud Native Computing Foundation 的首席技术官 Chris Aniszczyk 说,世界上许多巨头公司都为开源作出了贡献。他还提到,开源贡献者的指数是作为公司是否有所作为的参考。 科技巨头占据了这份榜单的主导地位:谷歌、微软、红帽、英特尔、IBM、亚马逊、Facebook、VMware、GitHub 和 SAP 依次是排名前 10 的贡献者。但Aniszczyk 表示,但也有很多终端用户公司进入前 100 名,包括 Uber、BBC、Orange、Netflix 和 Square。 “我们一直知道,在上游项目中工作不仅仅是关正确与否----它是开源软件开发的最佳方法,也是向客户提供开源福利的最佳方式”他说,“很高兴看到IT领导者们也认识到了这一点。” 为了和这些公司一起作出贡献,公司也需要有自己的开源策略,而拥有一个开源计划办公室则可以为其提供帮助。 “在使用开源软件方面,OPSO为公司提供了一个至关重要的能力中心”他说。 这与公司拥有安全运营中心的方式类似,他说。 “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与,这可以变成一个良性循环。” ——Tom Hickman,ThreatX 首席创新官 “如果你对安全团队进行相应投资,你通常是不会期望你的软件是安全的,也无法及时应对安全事件。”他说。 “同样的逻辑也适用于 OSPO,这就是为什么你会看到许多领先的公司,例如Apple、Meta、Twitter、Goldman Sachs、Bloomberg 和 Google 都拥有 OSPO。他们走在了趋势的前面。” 而对组织内的开源活动的支持态度亦可成为软件供应商们的差异化原因与营销的机会。 根据Red Hat 2月分发布的一项调查,82%的IT领导者更倾向于选择为开源社区作出贡献的软件供应商。 受访者表示,当供应商支持开源社区时,就表示着他们更熟悉开源的流程并且在客户遇到技术难题时会更加有效。 但收益的不仅仅是软件供应商们。 根据 The New Stack、Linux Foundation Research 和 TODO Group 9 月份的调查,57% 拥有 OSPO 的组织将使用它们来进一步发展战略关系和建立合作伙伴关系。 十年前,Mark Hinkle 在 Citrix 工作时创办了一个开源计划办公室。他指出了在内部拥有一个 OSPO将如何使公司受益。 “对于我们来说,最大的工作是让不熟悉开源的员工学会并参与其中,成为优秀的社区成员”,他说,“我们还就如何确保我们的IP不会在没有正确理解的情况下进入项目的情况提供了指导,并确保我们没有与我们企业软件许可相冲突的开源项目合作。” 他说,OSPO还帮助Citrix确定了公司参与开源项目和Linux基金会等贸易组织的战略机会。 如今,他是云原生开源集成平台 TriggerMesh 的首席执行官兼联合创始人。 他说,参与开源系统对公司来说有着重大的经济效益。 “我们参与Knative是为了分享我们基础底层平台的开发,但作为业务的一部分,我们也拥有相关的增值服务。”他说,“通过共享该平台的研发,这为我们提供了更多的资源来改进我们自己的差异化技术。” Part4如何入门开源 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月份调查中,有 63% 的公司表示,拥有OSPO 对其工程或产品团队的成功至关重要,高于上一年度该项研究数据的 54%。 其中77% 的人表示他们的开源程序对他们的软件实践产生了积极影响,例如提高了代码质量。 但公司也不可能总是为他们使用的每一个开源项目而花费精力。 “首先,节流一下”,VMware 的 Ambiel 建议道。 公司应该关注投入使用中最有意义的项目。而这也是OSPO可以帮助确定优先事项并确保技术与战略一致性的领域。 之后,开发人员应该自己去了解一下。项目通常提供相关在线文档,一般包含贡献着指南、治理文档和未解决问题列表。 “对于那些你较感兴趣的项目中,你可以介绍一下自己----打个招呼”,她说。“然后转到Slack频道或者分发列表,询问他们需要帮助的地方。也许他们不需要帮助,一切完好;又或者他们也有可能使用新人来审查核验代码。” Ambiel 说,开源计划办公室不仅可以帮助制定为开源社区做出贡献的商业案例,还可以帮助公司以安全、可靠和健全的方式来做这件事。 “如果我为一家公司工作,并想为开源做出贡献,我不想意外披露、泄露或破坏任何专利,”她说。“而OSPO可以帮助您做出明智的选择。” 她说,OSPO还可以在开源方面提供领导力和指导理念的支持。“它可以提供引领、指导、辅导和最佳实践的作用。” Aqua Security的开发人员倡导者Anaïs Urlichs则认为,支持开源的承诺必须从高层开始。 她说,“公司在多数时候往往不重视对开源的投资,所以员工自然而然不被鼓励对此作出贡献。” 在这些情况下,员工对于开源的热情也会在空闲时间里对开源的建设而消散殆尽,这对于开源的发展来说是不可持续的。 “如果公司对开源项目依赖度高,那么将开源贡献纳入工程师的日程安排是很重要的,”她说。“一些公司定义了员工可以为开源建设的时间百分比,将其作为他们正常工作日的一部分。” The New Stack 是 Insight Partners 的全资子公司,Insight Partners 是本文提到的以下公司的投资者:Sysdig、Aqua Security。 中英对照版 How an OSPO Can Help Your Engineers Give Back to Open Source OSPO (开源项目办公室)是如何使工程师回馈开源的 When it comes to open source software, there’s a big and growing problem: most organizations are takers, not givers. 谈到开源软件,有一个较大且日益严重的问题:大多数组织都是索取者,而不是给予者。 There’s a classic XKCD comic that shows a giant structure representing modern digital infrastructure, dependent on a tiny component created by “some random person in Nebraska” who has been “thanklessly maintaining since 2003.” 经典漫画XKCD展示了一个代表现代数字基础设施的巨大结构,它依赖于“内布拉斯加州的某位人士”创建的微小组件,该组件“自2003年来一直都处于吃力不讨好的状态”。 Randall Monroe’s XKCD comic illustrates the open source dilemma: overreliance on a small number of volunteer project maintainers. Randall Monroe 的XKCD漫画展示了目前开源面临的窘境:过度依赖少数项目维护志愿者的志愿服务。 This would have been funny, except that this is exactly what happened when security vulnerabilities were discovered in Log4j last December. (开源项目由志愿者自发来维护,)这听起来像是一件很滑稽的事情,但事实上去年十二月在Log4j中发现的安全漏洞也确实存在着上述情况。 The Java-based logging tool is ubiquitous in enterprise publications. In the last three months, for example, Log4j has been downloaded more than 30 million times, according to a report by the enterprise software company Sonatype. 然而这个基于Java的日志记录工具已经在企业内部刊物中无处不在。例如根据软件公司Sonatype的一份报告显示,在过去的三个月里,Log4j的下载量就已经超过3000万次。 The tool has 440,000 lines of code, according to Synopsys‘ Black Duck Open Hub research tool, with nearly 24,000 contributions by nearly 200 developers. That’s a large dev team compared to other open source projects. But looking closer at the numbers, more than 70% of commits were by just five people. 根据Synopsys(新思)公司旗下的Black Duck Open Hub 研究工具显示。Log4j有着440,000行代码,由近200名开发人员贡献了将近24,000行代码。其实与其他开源项目相比,这是一个庞大的开发团队。但是如果关注数据的话,就会发现超过70%的提交是仅仅靠五个人来完成的。 Log4j’s home page lists about a dozen members on its project team. Most projects have far fewer developers working on them — and that presents a problem for the organizations that depend on them. Log4j的主页上展示了十几位项目团队的成员。而大多项目的开发人员要比其原本需要的少得多----这是高度依赖开发人员团队所呈现出来的问题。 “There is little incentive for anyone today to contribute to an existing open source project,” said Jeremy Stretch, distinguished engineer at NS1, a DNS network company. “There’s usually no direct compensation, and few accolades are offered — most users don’t even know who maintains the software that they use.” “如今的人没有什么动力去为现有的开源项目做贡献”,来自DNS网络公司NS1的杰出工程师Jeremy Strech说,“因为通常来说,这没有直接的物质回报,也很少提供荣誉----大多数用户甚至不知道他们所用的软件是谁维护的。” The most common motivation among open source contributors is to add a feature that they themselves want to see, he said. “Once this has been achieved, the contributor rarely sticks around.” 他说,开源贡献者们最常见的动机就是添加他们自己想要的功能。“一旦实现了这一点,他们几乎都不会留下来。” Meanwhile, as a project becomes more popular, the burden on the core team of maintainers keeps increasing. 与此同时,随着项目的逐渐流行,对于维护方面的核心团队来说,他们的负担也在不断增加。 “More users means more feature requests and more bug reports — but not more maintainers,” Stretch said. “What was once an enjoyable hobby can quickly become a tedious chore, and many maintainers understandably opt to simply abandon their projects altogether.” “更多的用户意味有着更多的功能需求和错误报告----但不是更多的维护人员”,Stretch说。“曾经令人愉快的爱好很快就会变成一项乏味的项目,所以很多维护人员选择干脆完全放弃他们的项目,这也是可以理解的。” Part1The Tragedy of the Commons The open source software ecosystem is a perfect example of the “tragedy of the commons.” 开源软件的生态系统,就是“公地悲剧”的一个完美例子。 And the tragedy is — when everyone uses, but no one contributes, that resource — whether it’s an overrun park or an open source project — eventually collapses from overuse and underinvestment. Everyone loves using free stuff, but everyone expects someone else to take care of it. 这个悲剧就是---当一种资源,无论是一个超限的公园还是一个开源项目,所有人都在使用而没有人贡献之时,最终都会因为过度使用和投入不足而崩溃坍塌。 This approach can save you money in the short term, but it can become a fatal flaw over time. Especially since open source software is everywhere, running everything. 这种方式可以在短期内为你节省资金,但随着时间的推移,它可能会变成项目里致命的缺陷。 Linux, for example, the open source operating system, runs on 96% of the world’s top 1 million servers, and 90% of all cloud infrastructure is on Linux. Not to mention that 85% of all smartphones in the world run Linux, in the form of the Android OS. 拿Linux来说,这个开源操作系统在全球前100万台服务器中运行率在96%以上,且这些服务器90%的云基础设施也都在Linux上。更不用说世界上85%的智能手机都运行着Linux,即Android操作系统。 Then there’s Java, Apache, WordPress, Cassandra, Hadoop, MySQL, PHP, ElasticSearch, Kubernetes — the list of ubiquitous open source projects goes on and on. 还有Java, Apache, WordPress, Cassandra, Hadoop, MySQL, PHP, ElasticSearch, Kubernetes--这些常见开源项目的列表还在逐渐增加着。 Without open source, much of today’s technical infrastructure would immediately grind to a halt. 如果没有开源,今天的大部分技术基础设施的建设也将会戛然而止。 “It is a real problem,” said Danil Mikhailov, executive director at Data.org, a nonprofit backed by the Mastercard Center for Inclusive Growth and The Rockefeller Foundation that promotes the use of data science to tackle society’s greatest challenges. “这是一个很现实的问题”,Data.org的执行董事Danil Mikhailov说,该组织是由万事达包容性发展中心和洛克菲勒基金会支持,旨在促进使用数据科学来应对当今社会所面临的巨大挑战的非营利性组织。 While nearly all organizations use open source software, only a minority contribute to those projects. Forty-two percent of participants in a survey released in September by The New Stack, Linux Foundation Research, and the TODO Group said tthey contribute at least sometimes to open source projects. 虽然几乎所有组织都在使用着开源软件,但只有少数组织为这些项目作出了贡献。The New Stack、Linux Foundation Research 和 TODO Group 在 9 月发布的一项调查中,42% 的参与者表示,他们至少有时会为开源项目做出贡献。 The same study showed that only 36% of organizations train their engineers to contribute to open source. 而同一项研究表明,只有36%的组织会培训他们的工程师为开源作出贡献。 Individual companies should support projects that they use the most and are critical to their success, Mikhailov said: “If you use, you contribute.” 个体公司应该支持贡献这些他们使用最多且对他们成功至关重要的项目,Mikhailov认为:“如果你使用开源,你就应该为他做出属于你自己的贡献。” Part2OSPO Benefits:Less Tech Debt,Better Recruiting Participating in open source communities — especially when guided by an in-house open source program office (OSPO) — can help ensure the health of projects critical to your organization’s success, improve those projects’ security, and allow your engineers to have more impact in the projects’ development road map. 参与开源社区——特别是在内部开源项目办公室(OSPO)的指导下——不仅可以保证对组织成功至关重要项目的健康发展,还可以提高项目安全性,同时可以允许工程师在项目发展规划中起到更大的影响。 Say, for example, a company uses an open source tool and modifies it a little to make it better. If that improvement isn’t contributed back to the community, then the official version of the open source project will start to diverge from what the company is using 例如,如果一家公司使用了开源工具,并对其进行了一些调整使其变得更好。但如果这项改进没有反馈到开源社区,那么开源项目的正式版本就会一开始与该公司所使用的版本有所不同。 “You start to grow technical debt because when the original source changes and you’ve got a different version. Those differences grow rapidly, compounding daily. It doesn’t take long for you to be the proud user and maintainer of a one-of-a-kind open source project variant,” said Suzanne Ambiel, director, open source marketing and strategy at VMware. “当原始代码来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多。而这些差异是以天为单位迅速增长的。”VMware 开源营销和战略总监 Suzanne Ambiel 表示,“所以你很快就会变成一个开源项目里独一无二变体的‘自豪’用户和维护人员。” “The technical debt gets bigger and bigger and it gets very expensive for a company to manage.” “如果技术负债越来越多,那么公司的管理成本则会非常昂贵”。 Support for open source activity can also be a recruiting tool. “It’s really a talent magnet,” said Ambiel. “It’s one of the things that new hires look for.” 实际上对于开源活动的支持也变成了一种招聘途径。“这真是一块吸引人才的磁铁,”Ambiel说,“这也是新员工所寻求的“。 Some engineering managers might worry that open source contributions will detract from core product development, she said. Their rationale, she added, might run along the lines of, “I only have so much talent, and so many hours, and I need them to only work on things where I can measure and see the return on investment.” 她还提到,一些工程经理可能会对贡献开源而减损核心产品的开发的精力而感到担忧。她补充到,他们的理由有可能是这样的:“我只有有限的才华与时间,且我需要这些只做我认为可以度量且看到投资回报的事情。” But that attitude, she said, is shortsighted. Supporting employees who contribute to open source communities can build skills and develop talent, she said. 但她说,这是一种鼠目寸光的态度。支持开源社区并且作出贡献的员工,可以从中培养技能与增长才华。 Loris Degionni, chief technology officer and founder at Sysdig, a cloud security vendor, echoed this notion: “Finding employees who contribute to open source is a gold mine,” said. 云安全供应商 Sysdig 的首席技术官兼创始人 Loris Degionni 也赞同这一观点:“找出为开源做出贡献的员工无疑就找到一座金矿,”他说。 These employees are more capable of delivering features a company wants to use and merge them into community-supported standards, he said. And in a war for talent, companies that embrace open source are more attractive to developers. 他认为,这些参与开源的员工更具备公司想拥有的竞争力并将一些功能融入至社区所支持的标准中。且在人才争夺战中,拥抱开源的公司也更受到开发人员的青睐。 “Lastly, open source is driven by a community of technical experts you may not be able to hire,” he said. “When employees actively contribute and collaborate with these experts, they’ll be better informed of best practices and bring them back to your organization. “最后,开源项目是由你可能无法聘请的技术专家社区推动的”,他说,“当员工积极参与并于这些专家合作时,他们将能更好地深入这些最佳实践,并将这些收获带回到你的组织之中。” “You start to grow technical debt because when the original source changes and you’ve got a different version … It doesn’t take long for you to be the proud user and maintainer of a one-of-a-kind open source project variant.” —Suzanne Ambiel, director, open source marketing and strategy, VMware “当原始数据来源发生变化且你所使用的是不同的版本时,你的技术负债将越来越多...所以你很快就会变成一个开源项目里独一无二变体的”自豪“用户和维护人员。” — Suzanne Ambiel,VMware 开源营销和战略总监 “All of this should be rewarded — developers shouldn’t have to spend their free time honing their skills, as your company will quickly see benefits from their efforts.” “但是这一切终究不会白费--开发人员不应该把业余时间用在磨练他们的技能上,因为你的公司很快就会在他们的努力中看到好处。” An OSPO, Degionni suggested, can help achieve these goals, as well as help prioritize contributions and ensure collaboration. In addition, they can help provide governance that mirrors what companies would have for internally developed applications. Degionni认为,OSPO(开源计划办公室)可以帮助公司实现这些目标,以及帮助确定贡献的优先级并确保合作的进行。除此之外,他们也可以对公司内部开发应用程序方面的治理提供相关帮助。 “Members of the open source team are also in a position to be great internal evangelists for open source technologies, and act as bridges between the organization and the broader community,” he added. “开源团队的成员也可以成为开源技术的伟大内部布道师,并充当组织与更广泛社区之间的桥梁。”他补充道。 In the September survey from The New Stack, Linux Foundation Research and the TODO Group, nearly 53% of organizations with OSPOs said they saw more innovation as a result of having an OSPO, while almost 43% said they saw increased participation in external open source projects. 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月调查中,近 53% 的拥有 OSPO的组织表示,由于拥有了OSPO,他们看到了更多创新,而近 43% 的组织表示,他们在外部开源项目的参与度上有所增加。 Part3More OSPO Benefits:A Business Edge Contributing to open source communities doesn’t just help the communities, but the companies that contribute to them, said Tom Hickman, chief innovation officer at ThreatX, a cybersecurity firm. 网络安全公司 ThreatX 的首席创新官 Tom Hickman 表示,为开源社区做出贡献,不仅有助于社区,还有助于为社区做出贡献的公司。 “Growing the community of developers around a project helps the code base, and attracts more developers,” he said. “It can become a virtuous circle.” “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与”,他说,“这可以变成一个良性循环。” Also, companies that contribute to open source projects get twice the productive value from their use of open source than companies that don’t, according to research by Harvard Business School. 此外,根据哈佛商学院的研究,为开源项目作出贡献的公司从使用开源的项目中获得的生产价值,是不参与开源项目公司的两倍。 Many of the biggest companies in the world are contributing to open source, said Chris Aniszczyk, chief technology officer at Cloud Native Computing Foundation. He pointed to the Open Source Contributor Index as a reference for exactly just how much companies are doing. Cloud Native Computing Foundation 的首席技术官 Chris Aniszczyk 说,世界上许多巨头公司都为开源作出了贡献。他还提到,开源贡献者的指数是作为公司是否有所作为的参考。 The tech giants dominate the list: Google, Microsoft, Red Hat, Intel, IBM, Amazon, Facebook, VMware, GitHub and SAP are the top 10 contributors, in that order. But there are also a lot of end users on the top 100 list, said Aniszczyk, including Uber, the BBC, Orange, Netflix, and Square. 科技巨头占据了这份榜单的主导地位:谷歌、微软、红帽、英特尔、IBM、亚马逊、Facebook、VMware、GitHub 和 SAP 依次是排名前 10 的贡献者。但Aniszczyk 表示,但也有很多终端用户公司进入前 100 名,包括 Uber、BBC、Orange、Netflix 和 Square。 “We’ve always known working in upstream projects is not just the right thing to do —it’s the best approach to open source software development and the best way to deliver open source benefits to our customers,” he said. “It’s great to see that IT leaders recognize this as well.” “我们一直知道,在上游项目中工作不仅仅是关正确与否----它是开源软件开发的最佳方法,也是向客户提供开源福利的最佳方式“他说,“很高兴看到IT领导者们也认识到了这一点。” To contribute alongside these giants, companies need to have their own open source strategies, and having an open source program office can help. 为了和这些公司一起作出贡献,公司也需要有自己的开源策略,而拥有一个开源项目办公室则可以为其提供帮助。 “OSPOs provide a critical center of competency in a company when it comes to utilizing open source software,” he said. “在使用开源软件方面,OPSO为公司提供了一个至关重要的能力中心”他说。 It’s similar to the way that companies have security operations centers, he said. 这与公司拥有安全运营中心的方式类似,他说。 “Growing the community of developers around a project helps the code base, and attracts more developers. It can become a virtuous circle.” —Tom Hickman, chief innovation officer, ThreatX “围绕一个项目而发展的开发人员社区,有助于代码库的形成,并吸引更多的开发人员参与,这可以变成一个良性循环。” ——Tom Hickman,ThreatX 首席创新官 “If you don’t make the investment in a security team, you generally don’t expect your software to be secure or be able to respond to security incidents in a timely fashion,” he said. “如果你没有对安全团队进行相应投资,你通常是不会期望你的软件是安全的,也无法及时响应安全事件。”他说。 “The same logic applies to OSPOs and is why you see many leading companies out there such as Apple, Meta, Twitter, Goldman Sachs, Bloomberg, and Google all have OSPOs. They are ahead of the curve.” “同样的逻辑也适用于 OSPO,这就是为什么你会看到许多领先的公司,例如 Apple、Meta、Twitter、Goldman Sachs、Bloomberg 和 Google 都拥有 OSPO。他们走在了趋势的前面。” Support for open source activity within your organization can become a differentiator and marketing opportunity for software vendors. 而对组织内的开源活动的支持态度亦可成为软件供应商们的差异化原因与营销的机会。 According to a Red Hat survey released in February, 82% of IT leaders are more likely to select a vendor who contributes to the open source community. 根据Red Hat2月分发布的一项调查,82%的IT领导者更倾向于选择为开源社区作出贡献的软件供应商。 Respondents said that when vendors support open source communities they are more familiar with open source processes and are more effective if customers have technical challenges. 受访者表示,当供应商支持开源社区时,就表示着他们更熟悉开源的流程并且在客户遇到技术难题时会更加有效。 But it’s not just software vendors who benefit. 但收益的不仅仅是软件供应商们。 According to September’s survey by The New Stack, Linux Foundation Research, and the TODO Group, 57% of organizations with OSPOs use them to further strategic relationships and build partnerships. 根据 The New Stack、Linux Foundation Research 和 TODO Group 9 月份的调查,57% 拥有 OSPO 的组织将使用它们来进一步发展战略关系和建立合作伙伴关系。 Mark Hinkle started an open source program office back when he worked at Citrix a decade ago. He pointed out how having an OSPO in-house benefited the company. 十年前,Mark Hinkle 在 Citrix 工作时创办了一个开源计划办公室。他指出了在内部拥有一个 OSPO将如何使公司受益。 “For us the biggest job was to educate our employees who weren’t familiar with open source to get involved and be good community members,” he said. “We also provided guidance on how to make sure our IP didn’t enter projects without proper understanding and we made sure we didn’t incorporate open source that conflicted with our enterprise software licensing.” “对于我们来说,最大的工作是让不熟悉开源的员工学会并参与其中,成为优秀的社区成员”,他说,“我们还就如何确保我们的IP不会在没有正确理解的情况下进入项目的情况提供了指导,并确保我们没有与我们企业软件许可相冲突的开源项目合作。” The OSPO also helped Citrix identify strategic opportunities for the company to participate in open source projects and trade organizations like The Linux Foundation, he said. 他说,OSPO还帮助Citrix确定了公司参与开源项目和Linux基金会等贸易组织的战略机会。 Today, he’s the CEO and co-founder of TriggerMesh, a cloud native, open source integration platform. 如今,他是云原生开源集成平台 TriggerMesh 的首席执行官兼联合创始人。 There are some significant economic benefits to participating in the open source ecosystem, he said. 他说,参与开源系统对公司来说有着重大的经济效益。 “We participate in Knative to share the development of our underlying platform but we develop value-added services as part of our business,” he said. “By sharing the R and D for the platform, it gives us more resources to develop our own differentiated technology.” “我们参与Knative是为了分享我们基础底层平台的开发,但作为业务的一部分,我们也拥有相关的增值服务。”他说,“通过共享该平台的研发,这为我们提供了更多的资源来改进我们自己的差异化技术。” Part4How to Get Started in Open Source Sixty-three percent of companies in the September survey from The New Stack, Linux Foundation Research and the TODO Group said that having an OSPO was very or extremely critical to the success of their engineering or product teams, up from 54% in the previous annual study. 在 The New Stack、Linux Foundation Research 和 TODO Group 的 9 月份调查中,有 63% 的公司表示,拥有OSPO 对其工程或产品团队的成功至关重要,高于上一年度该项研究数据的 54%。 In particular, 77% said that their open source program had a positive impact on their software practices, such as improved code quality. 其中77% 的人表示他们的开源程序对他们的软件实践产生了积极影响,例如提高了代码质量。 But companies can’t always contribute to every single open source project that they use. 但公司也不可能总是为他们使用的每一个开源项目而花费精力。 “First, thin the herd a little bit,” advised VMware’s Ambiel. “首先,节流一下”,VMware 的 Ambiel 建议道。 Companies should look at the projects that make the most sense for their use cases. This is an area where an OSPO can help set priorities and ensure technical and strategic alignment. 公司应该关注投入使用中最有意义的项目。而这也是OSPO可以帮助确定优先事项并确保技术与战略一致性的领域。 Then, developers should go and check out the projects themselves. Projects typically offer online documentation, often with contributor guides, governance documents, and lists of open issues. 之后,开发人员应该自己去了解一下。项目通常提供相关在线文档,一般包含贡献着指南、治理文档和未解决问题列表。 “For the projects that rise to the top of your strategic list, introduce yourself — say hello,” she said. “Go to the Slack channel or the distribution list and ask where they need help. Maybe they don’t need help and everything is good. Or maybe they can use a new person to review code.” “对于那些上升到你的战略清单顶端的项目,你可以介绍一下自己----打个招呼”,她说。“然后转到Slack频道或者分发列表,询问他们需要帮助的地方。也许他们不需要帮助,一切完好;又或者他们也有可能使用新人来审查核验代码。” An open source program office can not only help make a business case for contributing to the open source community, Ambiel said, but can help companies do it in a way that’s safe, secure and sound. Ambiel 说,开源项目办公室不仅可以帮助制定为开源社区做出贡献的商业案例,还可以帮助公司以安全、可靠和健全的方式来做这件事。 “If I work for a company and want to contribute to open source, I don’t want to accidentally disclose, divulge or undermine any patents,” she said. “An OSPO helps you make smart choices.” “如果我为一家公司工作,并想为开源做出贡献,我不想意外披露、泄露或破坏任何专利,”她说。“而OSPO可以帮助您做出明智的选择。” An OSPO can also help provide leadership and the guiding philosophy about supporting open source, she said. “It can provide guidance, mentorship, coaching and best practices.” 她说,OSPO还可以在开源方面提供领导力和指导理念的支持。“它可以提供引领、指导、辅导和最佳实践的作用。” Commitment to support open source has to start at the top, said Anaïs Urlichs, developer advocate at Aqua Security. Aqua Security的开发人员倡导者Anaïs Urlichs则认为,支持开源的承诺必须从高层开始。 “Too often,” she said, “companies do not value investment into open source, so employees are not encouraged to contribute to it.” 她说,“公司在多数时候往往不重视对开源的投资,所以员工自然而然不被鼓励对此作出贡献。” In those cases, employees with a passion for open source end up contributing during their free time, which is not sustainable. 在这些情况下,员工对于开源的热情也会在空闲时间里对开源的建设而消散殆尽,这对于开源的发展来说是不可持续的。 “If companies rely on open source projects, it is important to make open source contributions part of an engineer’s work schedule,” she said. “Some companies define a time percentage that employees can contribute to open source as part of their normal workday.” “如果公司对开源项目依赖度高,那么将开源贡献纳入工程师的日程安排是很重要的,”她说。“一些公司定义了员工可以为开源建设的时间百分比,将其作为他们正常工作日的一部分。” The New Stack is a wholly owned subsidiary of Insight Partners, an investor in the following companies mentioned in this article: Sysdig, Aqua Security. The New Stack 是 Insight Partners 的全资子公司,Insight Partners 是本文提到的以下公司的投资者:Sysdig、Aqua Security。 相关阅读 | Related Reading 《开源合规指南(企业篇)》正式发布,为推动我国开源合规建设提供参考 “目标->用户->指标”——企业开源运营之道|瞰道@谭中意 开源之夏邀请函——仅限高校学子开启 开源社简介 开源社成立于 2014 年,是由志愿贡献于开源事业的个人成员,依 “贡献、共识、共治” 原则所组成,始终维持厂商中立、公益、非营利的特点,是最早以 “开源治理、国际接轨、社区发展、开源项目” 为使命的开源社区联合体。开源社积极与支持开源的社区、企业以及政府相关单位紧密合作,以 “立足中国、贡献全球” 为愿景,旨在共创健康可持续发展的开源生态,推动中国开源社区成为全球开源体系的积极参与及贡献者。 2017 年,开源社转型为完全由个人成员组成,参照 ASF 等国际顶级开源基金会的治理模式运作。近八年来,链接了数万名开源人,集聚了上千名社区成员及志愿者、海内外数百位讲师,合作了近百家赞助、媒体、社区伙伴。 本篇文章为转载内容。原文链接:https://blog.csdn.net/kaiyuanshe/article/details/124976824。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-05-03 09:19:23
273
转载
转载文章
...放视频,该接口在课程管理服务中开发,供学习服务进行远程调用。 在学习服务中远程调用 课程计划媒资信息查询接口,获取该课程计划的视频播放的 m3u8 url地址,并返回给前端,前端使用该 url 进行视频的在线播放。 在线学习完整的测试流程:媒资信息的上传、选择、发布到前端门户、搜索门户测试,在线学习的播放视频。 目录 内容会比较多,小伙伴门可以根据目录进行按需查阅。 文章目录 😎 知识点概览 目录 一、学习页面:查询课程计划 0x01 需求分析 0x02 Api接口 0x03 服务端开发 Controller Service 测试 0x04 前端开发 配置NGINX虚拟主机 前端 API 方法 前端 API 方法调用 测试 二、学习页面:获取视频播放地址 0x01 需求分析 0x02 课程发布:储存媒资信息 需求分析 数据模型 Dao Service 测试 0x03 Logstash:扫描课程计划媒资 创建索引 创建模板文件 配置 mysql.conf 启动 logstash.bat Logstash多实例运行 0x04 搜素服务:查询课程媒资接口 需求分析 Api接口定义 Service Controller 测试 三、在线学习:接口开发 0x01 需求分析 0x02 搭建开发环境 0x03 Api接口 0x04 服务端开发 需求分析 搜索服务注册Eureka 搜索服务客户端 自定义错误代码 Service Controller 测试 0x05 前端开发 需求分析 api方法 配置代理 视频播放页面 简单的测试 完整的测试 1、上传文件 一些问题 ~~方案1:删除本地分块文件重新尝试上传~~ 方案2:检查前端提交的MD5值是否正确 2、为课程计划选择媒资信息 3、前端门户测试 四、待完善的一些功能 😁 认识作者 一、学习页面:查询课程计划 0x01 需求分析 到目前为止,我们已可以编辑课程计划信息并上传课程视频,下一步我们要实现在线学习页面动态读取章节对应的视频并进行播放。在线学习页面所需要的信息有两类: 课程计划信息 课程学习信息(视频地址、学习进度等) 如下图: 在线学习集成媒资管理的需求如下: 1、在线学习页面显示课程计划 2、点击课程计划播放该课程计划对应的视频 本章节实现学习页面动态显示课程计划,进入不同课程的学习页面右侧动态显示当前课程的课程计划。 0x02 Api接口 课程计划信息从哪里获取? 在课程发布完成后会自动发布到一个 course_pub 的表中,logstash 会自动将课程发布后的信息自动采集到 ES 索引库中,这些信息也包含课程计划信息。 所以考虑性能要求,课程发布后对课程的查询统一从 ES 索引库中查询。 前端通过请求 搜索服务 获取课程信息,需要单独在 搜索服务 中定义课程信息查询接口。 本接口接收课程id,查询课程所有信息返回给前端。 我们在搜素服务 API 下添加以下方法 @ApiOperation("根据id搜索课程发布信息")public Map<String,CoursePub> getdetail(String id); 返回的课程信息为 json 结构:key 为课程id,value 为课程内容。 0x03 服务端开发 在搜索服务中开发查询课程信息接口。 Controller 在搜素服务下添加以下方法 / 根据id搜索课程发布信息 @param id 课程id @return JSON数据/@Override@GetMapping("/getdetail/{id}")public Map<String, CoursePub> getdetail(@PathVariable("id")String id) {return esCourseService.getdetail(id);} Service / 根据id搜索课程发布信息 @param id 课程id @return JSON数据/public Map<String, CoursePub> getdetail(String id) {//设置索引SearchRequest searchRequest = new SearchRequest(es_index);//设置类型searchRequest.types(es_type);//创建搜索源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();//设置查询条件,根据id进行查询searchSourceBuilder.query(QueryBuilders.termQuery("id",id));//这里不使用source的原字段过滤,查询所有字段// searchSourceBuilder.fetchSource(new String[]{"name", "grade", "charge","pic"}, newString[]{});//设置搜索源对象searchRequest.source(searchSourceBuilder);//执行搜索SearchResponse searchResponse = null;try {searchResponse = restHighLevelClient.search(searchRequest);} catch (IOException e) {e.printStackTrace();}//获取搜索结果SearchHits hits = searchResponse.getHits();SearchHit[] searchHits = hits.getHits(); //获取最优结果Map<String,CoursePub> map = new HashMap<>();for (SearchHit hit: searchHits) {//从搜索结果中取值并添加到coursePub对象Map<String, Object> sourceAsMap = hit.getSourceAsMap();String courseId = (String) sourceAsMap.get("id");String name = (String) sourceAsMap.get("name");String grade = (String) sourceAsMap.get("grade");String charge = (String) sourceAsMap.get("charge");String pic = (String) sourceAsMap.get("pic");String description = (String) sourceAsMap.get("description");String teachplan = (String) sourceAsMap.get("teachplan");CoursePub coursePub = new CoursePub();coursePub.setId(courseId);coursePub.setName(name);coursePub.setPic(pic);coursePub.setGrade(grade);coursePub.setTeachplan(teachplan);coursePub.setDescription(description);//设置map对象map.put(courseId,coursePub);}return map;} 测试 使用 swagger-ui 或 postman 测试查询课程信息接口。 0x04 前端开发 配置NGINX虚拟主机 学习中心的二级域名为 ucenter.xuecheng.com ,我们在 nginx 中配置 ucenter 虚拟主机。 学成网用户中心server {listen 80;server_name ucenter.xuecheng.com;个人中心location / {proxy_pass http://ucenter_server_pool;} } 前端ucenterupstream ucenter_server_pool{server 127.0.0.1:7081 weight=10;server 127.0.0.1:13000 weight=10;} 在学习中心要调用搜索的 API,使用 Nginx 解决代理,如下图: 在 ucenter 虚拟主机下配置搜索 Api 代理路径 后台搜索(公开api)upstream search_server_pool{server 127.0.0.1:40100 weight=10;} 学成网用户中心server {listen 80;server_name ucenter.xuecheng.com;个人中心location / {proxy_pass http://ucenter_server_pool;}后端搜索服务location /openapi/search/ {proxy_pass http://search_server_pool/search/;} } 前端 API 方法 在学习中心 xc-ui-pc-leanring 对课程信息的查询属于基础常用功能,所以我们将课程查询的 api 方法定义在base 模块下,如下图: 在system.js 中定义课程查询方法: import http from './public'export const course_view = id => {return http.requestGet('/openapi/search/course/getdetail/'+id);} 前端 API 方法调用 在 learning_video.vue 页面中调用课程信息查询接口得到课程计划,将课程计划json 串转成对象。 xc-ui-pc-leanring/src/module/course/page/learning_video.vue 1、定义视图 课程计划 <!--课程计划部分代码--><div class="navCont"><div class="course-weeklist"><div class="nav nav-stacked" v-for="(teachplan_first, index) in teachplanList"><div class="tit nav-justified text-center"><i class="pull-left glyphicon glyphicon-th-list"></i>{ {teachplan_first.pname} }<i class="pull-right"></i></div><li v-if="teachplan_first.children!=null" v-for="(teachplan_second, index) in teachplan_first.children"><i class="glyphicon glyphicon-check"></i><a :href="url" @click="study(teachplan_second.id)">{ {teachplan_second.pname} }</a></li><!-- <div class="tit nav-justified text-center"><i class="pull-left glyphicon glyphicon-th-list"></i>第一章<i class="pull-right"></i></div><li ><i class="glyphicon glyphicon-check"></i><a :href="url" >第一节</a></li>--><!--<li><i class="glyphicon glyphicon-unchecked"></i>为什么分为A、B、C部分</li>--></div></div></div> 课程名称 <div class="top text-center">{ {coursename} }</div> 定义数据对象 data() {return {url:'',//当前urlcourseId:'',//课程idchapter:'',//章节Idcoursename:'',//课程名称coursepic:'',//课程图片teachplanList:[],//课程计划playerOptions: {//播放参数autoplay: false,controls: true,sources: [{type: "application/x-mpegURL",src: ''}]},} } 在 created 钩子方法中获取课程信息 created(){//当前请求的urlthis.url = window.location//课程idthis.courseId = this.$route.params.courseId//章节idthis.chapter = this.$route.params.chapter//查询课程信息systemApi.course_view(this.courseId).then((view_course)=>{if(!view_course || !view_course[this.courseId]){this.$message.error("获取课程信息失败,请重新进入此页面!")return ;} let courseInfo = view_course[this.courseId]console.log(courseInfo)this.coursename = courseInfo.nameif(courseInfo.teachplan){let teachplan = JSON.parse(courseInfo.teachplan);this.teachplanList = teachplan.children;} })}, 测试 在浏览器请求:http://ucenter.xuecheng.com//learning/4028e581617f945f01617f9dabc40000/0 4028e581617f945f01617f9dabc40000:第一个参数为课程 id,测试时从 ES索引库找一个课程 id 0:第二个参数为课程计划 id,此参数用于点击课程计划播放视频。 如果出现跨域问题,但是确定已经配置了跨域,请尝试结束所以 nginx.exe 的进程 和 清空浏览器缓存。 如果还没有解决?重启电脑试试。 二、学习页面:获取视频播放地址 0x01 需求分析 用户进入在线学习页面,点击课程计划将播放该课程计划对应的教学视频。 业务流程如下: 业务流程说明: 1、用户进入在线学习页面,页面请求搜索服务获取课程信息(包括课程计划信息)并且在页面展示。 2、在线学习请求学习服务获取视频播放地址。 3、学习服务校验当前用户是否有权限学习,如果没有权限学习则提示用户。 4、学习服务校验通过,请求搜索服务获取课程媒资信息。 5、搜索服务请求ElasticSearch获取课程媒资信息。 为什么要请求 ElasticSearch 查询课程媒资信息? 出于性能的考虑,公开查询课程信息从搜索服务查询,分摊 mysql 数据库的访问压力。 什么时候将课程媒资信息存储到 ElasticSearch 中? 课程媒资信息是在课程发布的时候存入 ElasticSearch,因为课程发布后课程信息将基本不再修改。 0x02 课程发布:储存媒资信息 需求分析 课程媒资信息是在课程发布的时候存入 ElasticSearch 索引库,因为课程发布后课程信息将基本不再修改,具体的业务流程如下。 1、课程发布,向课程媒资信息表写入数据。 1)根据课程 id 删除 teachplanMediaPub 中的数据 2)根据课程 id 查询 teachplanMedia 数据 3)将查询到的 teachplanMedia 数据插入到 teachplanMediaPub 中 2、Logstash 定时扫描课程媒资信息表,并将课程媒资信息写入索引库。 数据模型 在 xc_course 数据库创建课程计划媒资发布表: CREATE TABLE teachplan_media_pub (teachplan_id varchar(32) NOT NULL COMMENT '课程计划id',media_id varchar(32) NOT NULL COMMENT '媒资文件id',media_fileoriginalname varchar(128) NOT NULL COMMENT '媒资文件的原始名称',media_url varchar(256) NOT NULL COMMENT '媒资文件访问地址',courseid varchar(32) NOT NULL COMMENT '课程Id',timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT'logstash使用',PRIMARY KEY (teachplan_id)) ENGINE=InnoDB DEFAULT CHARSET=utf8 数据模型类如下: package com.xuecheng.framework.domain.course;import lombok.Data;import lombok.ToString;import org.hibernate.annotations.GenericGenerator;import javax.persistence.;import java.io.Serializable;import java.util.Date;@Data@ToString@Entity@Table(name="teachplan_media_pub")@GenericGenerator(name = "jpa-assigned", strategy = "assigned")public class TeachplanMediaPub implements Serializable {private static final long serialVersionUID = -916357110051689485L;@Id@GeneratedValue(generator = "jpa-assigned")@Column(name="teachplan_id")private String teachplanId;@Column(name="media_id")private String mediaId;@Column(name="media_fileoriginalname")private String mediaFileOriginalName;@Column(name="media_url")private String mediaUrl;@Column(name="courseid")private String courseId;@Column(name="timestamp")private Date timestamp;//时间戳} Dao 创建 TeachplanMediaPub 表的 Dao,向 TeachplanMediaPub 存储信息采用先删除该课程的媒资信息,再添加该课程的媒资信息,所以这里定义根据课程 id 删除课程计划媒资方法: public interface TeachplanMediaPubRepository extends JpaRepository<TeachplanMediaPub, String> {//根据课程id删除课程计划媒资信息long deleteByCourseId(String courseId);} 从TeachplanMedia查询课程计划媒资信息 //从TeachplanMedia查询课程计划媒资信息public interface TeachplanMediaRepository extends JpaRepository<TeachplanMedia, String> {List<TeachplanMedia> findByCourseId(String courseId);} Service 编写保存课程计划媒资信息方法,并在课程发布时调用此方法。 1、保存课程计划媒资信息方法 本方法采用先删除该课程的媒资信息,再添加该课程的媒资信息,在 CourseService 下定义该方法 //保存课程计划媒资信息private void saveTeachplanMediaPub(String courseId){//查询课程媒资信息List<TeachplanMedia> byCourseId = teachplanMediaRepository.findByCourseId(courseId);if(byCourseId == null) return; //没有查询到媒资数据则直接结束该方法//将课程计划媒资信息储存到待索引表//删除原有的索引信息teachplanMediaPubRepository.deleteByCourseId(courseId);//一个课程可能会有多个媒资信息,遍历并使用list进行储存List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();for (TeachplanMedia teachplanMedia: byCourseId) {TeachplanMediaPub teachplanMediaPub = new TeachplanMediaPub();BeanUtils.copyProperties(teachplanMedia, teachplanMediaPub);teachplanMediaPubList.add(teachplanMediaPub);}//保存所有信息teachplanMediaPubRepository.saveAll(teachplanMediaPubList);} 2、课程发布时调用此方法 修改课程发布的 coursePublish 方法: ....//保存课程计划媒资信息到待索引表saveTeachplanMediaPub(courseId);//页面urlString pageUrl = cmsPostPageResult.getPageUrl();return new CoursePublishResult(CommonCode.SUCCESS,pageUrl);..... 测试 测试课程发布后是否成功将课程媒资信息存储到 teachplan_media_pub 中,测试流程如下: 1、指定一个课程 2、为课程计划添加课程媒资 3、执行课程发布 4、观察课程计划媒资信息是否存储至 teachplan_media_pub 中 注意:由于此测试仅用于测试发布课程计划媒资信息的功能,可暂时将 cms页面发布的功能暂时屏蔽,提高测试效率。 测试结果如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vrzs5589-1595567273126)(https://qnoss.codeyee.com/20200704_15/image7)] 0x03 Logstash:扫描课程计划媒资 Logstash 定时扫描课程媒资信息表,并将课程媒资信息写入索引库。 创建索引 1、创建 xc_course_media 索引 2、并向此索引创建如下映射 POST: http://localhost:9200/xc_course_media/doc/_mapping {"properties" : {"courseid" : {"type" : "keyword"},"teachplan_id" : {"type" : "keyword"},"media_id" : {"type" : "keyword"},"media_url" : {"index" : false,"type" : "text"},"media_fileoriginalname" : {"index" : false,"type" : "text"} }} 索引创建成功 创建模板文件 在 logstach 的 config 目录文件 xc_course_media_template.json 文件路径为 %ES_ROOT_DIR%/logstash6.8.8/config/xc_course_media_template.json %ES_ROOT_DIR% 为 ElasticSearch 和 logstash 的安装目录 内容如下: {"mappings" : {"doc" : {"properties" : {"courseid" : {"type" : "keyword"},"teachplan_id" : {"type" : "keyword"},"media_id" : {"type" : "keyword"},"media_url" : {"index" : false,"type" : "text"},"media_fileoriginalname" : {"index" : false,"type" : "text"} }},"template" : "xc_course_media"} } 配置 mysql.conf 在logstash的 config 目录下配置 mysql_course_media.conf 文件供 logstash 使用,logstash 会根据 mysql_course_media.conf 文件的配置的地址从 MySQL 中读取数据向 ES 中写入索引。 参考https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html 配置输入数据源和输出数据源。 input {stdin {} jdbc {jdbc_connection_string => "jdbc:mysql://localhost:3306/xc_course?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC" 数据库信息jdbc_user => "root"jdbc_password => "123123" MYSQL 驱动地址,修改为maven仓库对应的位置jdbc_driver_library => "D:/soft/apache-maven-3.5.4/repository/mysql/mysql-connector-java/5.1.40/mysql-connector-java-5.1.40.jar" the name of the driver class for mysqljdbc_driver_class => "com.mysql.jdbc.Driver"jdbc_paging_enabled => "true"jdbc_page_size => "50000"要执行的sql文件statement_filepath => "/conf/course.sql"statement => "select from teachplan_media_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)"定时配置schedule => " "record_last_run => truelast_run_metadata_path => "D:/soft/elasticsearch/logstash-6.8.8/config/xc_course_media_metadata"} } output {elasticsearch {ES的ip地址和端口hosts => "localhost:9200"hosts => ["localhost:9200","localhost:9202","localhost:9203"]ES索引库名称index => "xc_course_media"document_id => "%{teachplan_id}"document_type => "doc"template => "D:/soft/elasticsearch/logstash-6.8.8/config/xc_course_media_template.json"template_name =>"xc_course_media"template_overwrite =>"true"} stdout {日志输出codec => json_lines} } 启动 logstash.bat 启动 logstash.bat 采集 teachplan_media_pub 中的数据,向 ES 写入索引。 logstash.bat -f ../config/mysql_course_media.conf 课程发布成功后,Logstash 会自动参加 teachplan_media_pub 表中新增的数据,效果如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ILPBxfXi-1595567273134)(https://qnoss.codeyee.com/20200704_15/image10)] Logstash多实例运行 由于之前我们还启动了一个 Logstash 对课程的发布信息进行采集,所以如果想两个 logstash 实例同时运行,因为每个实例都有一个.lock文件,所以不能使用同一个目录来存放数据,所以我们需要使用 --path.data= 为每个实例指定单独的数据目录,具体的代码如下: 该配置是在windows下进行的 课程发布实例 logstash_start_course_pub.bat @title logstash in course_publogstash.bat -f ..\config\mysql.conf --path.data=../data/course_pub 课程计划媒体发布实例 logstash_start_teachplan_media.bat @title logstash i n teachplan_media_publogstash.bat -f ../config/mysql_course_media.conf --path.data=../data/teachplan_media/ 同时运行效果如下 0x04 搜素服务:查询课程媒资接口 需求分析 搜索服务 提供查询课程媒资接口,此接口供学习服务调用。 Api接口定义 @ApiOperation("根据课程计划查询媒资信息")public TeachplanMediaPub getmedia(String teachplanId); Service 1、配置课程计划媒资索引库等信息 在 application.yml 中配置 xuecheng:elasticsearch:hostlist: ${eshostlist:127.0.0.1:9200} 多个结点中间用逗号分隔course:index: xc_coursetype: docsource_field: id,name,grade,mt,st,charge,valid,pic,qq,price,price_old,status,studymodel,teachmode,expires,pub_time,start_time,end_timemedia:index: xc_course_mediatype: docsource_field: courseid,media_id,media_url,teachplan_id,media_fileoriginalname 2、service 方法开发 在 课程搜索服务 中定义课程媒资查询接口,为了适应后续需求,service 参数定义为数组,可一次查询多个课程计划的媒资信息。 / 根据一个或者多个课程计划id查询媒资信息 @param teachplanIds 课程id @return QueryResponseResult/public QueryResponseResult<TeachplanMediaPub> getmedia(String [] teachplanIds){//设置索引SearchRequest searchRequest = new SearchRequest(media_index);//设置类型searchRequest.types(media_type);//创建搜索源对象SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();//源字段过滤String[] media_index_arr = media_field.split(",");searchSourceBuilder.fetchSource(media_index_arr, new String[]{});//查询条件,根据课程计划id查询(可以传入多个课程计划id)searchSourceBuilder.query(QueryBuilders.termsQuery("teachplan_id", teachplanIds));searchRequest.source(searchSourceBuilder);SearchResponse searchResponse = null;try {searchResponse = restHighLevelClient.search(searchRequest);} catch (IOException e) {e.printStackTrace();}//获取结果SearchHits hits = searchResponse.getHits();long totalHits = hits.getTotalHits();SearchHit[] searchHits = hits.getHits();//数据列表List<TeachplanMediaPub> teachplanMediaPubList = new ArrayList<>();for(SearchHit hit:searchHits){TeachplanMediaPub teachplanMediaPub =new TeachplanMediaPub();Map<String, Object> sourceAsMap = hit.getSourceAsMap();//取出课程计划媒资信息String courseid = (String) sourceAsMap.get("courseid");String media_id = (String) sourceAsMap.get("media_id");String media_url = (String) sourceAsMap.get("media_url");String teachplan_id = (String) sourceAsMap.get("teachplan_id");String media_fileoriginalname = (String) sourceAsMap.get("media_fileoriginalname");teachplanMediaPub.setCourseId(courseid);teachplanMediaPub.setMediaUrl(media_url);teachplanMediaPub.setMediaFileOriginalName(media_fileoriginalname);teachplanMediaPub.setMediaId(media_id);teachplanMediaPub.setTeachplanId(teachplan_id);//将对象加入到列表中teachplanMediaPubList.add(teachplanMediaPub);}//构建返回课程媒资信息对象QueryResult<TeachplanMediaPub> queryResult = new QueryResult<>();queryResult.setList(teachplanMediaPubList);queryResult.setTotal(totalHits);return new QueryResponseResult<TeachplanMediaPub>(CommonCode.SUCCESS,queryResult);} Controller / 根据课程计划id搜索发布后的媒资信息 @param teachplanId @return/@GetMapping(value="/getmedia/{teachplanId}")@Overridepublic TeachplanMediaPub getmedia(@PathVariable("teachplanId") String teachplanId) {//为了service的拓展性,所以我们service接收的是数组作为参数,以便后续开发查询多个ID的接口String[] teachplanIds = new String[]{teachplanId};//通过service查询ES获取课程媒资信息QueryResponseResult<TeachplanMediaPub> mediaPubQueryResponseResult = esCourseService.getmedia(teachplanIds);QueryResult<TeachplanMediaPub> queryResult = mediaPubQueryResponseResult.getQueryResult();if(queryResult!=null&& queryResult.getList()!=null&& queryResult.getList().size()>0){//返回课程计划对应课程媒资return queryResult.getList().get(0);} return new TeachplanMediaPub();} 测试 使用 swagger-ui 和 postman 测试课程媒资查询接口。 三、在线学习:接口开发 0x01 需求分析 根据下边的业务流程,本章节完成前端学习页面请求学习服务获取课程视频地址,并自动播放视频。 0x02 搭建开发环境 1、创建数据库 创建 xc_learning 数据库,学习数据库将记录学生的选课信息、学习信息。 导入:资料/xc_learning.sql 2、创建学习服务工程 参考课程管理服务工程结构,创建学习服务工程: 导入:资料/xc-service-learning.zip 项目工程结构如下 0x03 Api接口 此 api 接口是课程学习页面请求学习服务获取课程学习地址。 定义返回值类型: package com.xuecheng.framework.domain.learning.response;import com.xuecheng.framework.model.response.ResponseResult;import com.xuecheng.framework.model.response.ResultCode;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;@Data@ToString@NoArgsConstructorpublic class GetMediaResult extends ResponseResult {public GetMediaResult(ResultCode resultCode, String fileUrl) {super(resultCode);this.fileUrl = fileUrl;}//媒资文件播放地址private String fileUrl;} 定义接口,学习服务根据传入课程 ID、章节 Id(课程计划 ID)来取学习地址。 @Api(value = "录播课程学习管理",description = "录播课程学习管理")public interface CourseLearningControllerApi {@ApiOperation("获取课程学习地址")public GetMediaResult getMediaPlayUrl(String courseId,String teachplanId);} 0x04 服务端开发 需求分析 学习服务根据传入课程ID、章节Id(课程计划ID)请求搜索服务获取学习地址。 搜索服务注册Eureka 学习服务要调用搜索服务查询课程媒资信息,所以需要将搜索服务注册到 eureka 中。 1、查看服务名称是否为 xc-service-search 注意修改application.xml中的服务名称:spring:application:name: xc‐service‐search 2、配置搜索服务的配置文件 application.yml,加入 Eureka 配置 如下: eureka:client:registerWithEureka: true 服务注册开关fetchRegistry: true 服务发现开关serviceUrl: Eureka客户端与Eureka服务端进行交互的地址,多个中间用逗号分隔defaultZone: ${EUREKA_SERVER:http://localhost:50101/eureka/,http://localhost:50102/eureka/}instance:prefer-ip-address: true 将自己的ip地址注册到Eureka服务中ip-address: ${IP_ADDRESS:127.0.0.1}instance-id: ${spring.application.name}:${server.port} 指定实例idribbon:MaxAutoRetries: 2 最大重试次数,当Eureka中可以找到服务,但是服务连不上时将会重试,如果eureka中找不到服务则直接走断路器MaxAutoRetriesNextServer: 3 切换实例的重试次数OkToRetryOnAllOperations: false 对所有操作请求都进行重试,如果是get则可以,如果是post,put等操作没有实现幂等的情况下是很危险的,所以设置为falseConnectTimeout: 5000 请求连接的超时时间ReadTimeout: 6000 请求处理的超时时间 3、添加 eureka 依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring‐cloud‐starter‐netflix‐eureka‐client</artifactId></dependency> 4、修改启动类,在class上添加如下注解: @EnableDiscoveryClient 搜索服务客户端 在 学习服务 创建搜索服务的客户端接口,此接口会生成代理对象,调用搜索服务: package com.xuecheng.learning.client;import com.xuecheng.framework.domain.course.TeachplanMediaPub;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;@FeignClient(value = "xc‐service‐search")public interface CourseSearchClient {@GetMapping(value="/getmedia/{teachplanId}")public TeachplanMediaPub getmedia(@PathVariable("teachplanId") String teachplanId);} 自定义错误代码 我们在 com.xuecheng.framework.domain.learning.response 包下自定义一个错误消息模型 package com.xuecheng.framework.domain.learning.response;import com.xuecheng.framework.model.response.ResultCode;import lombok.ToString;@ToStringpublic enum LearningCode implements ResultCode {LEARNING_GET_MEDIA_ERROR(false,23001,"学习中心获取媒资信息错误!");//操作代码boolean success;//操作代码int code;//提示信息String message;private LearningCode(boolean success, int code, String message){this.success = success;this.code = code;this.message = message;}@Overridepublic boolean success() {return success;}@Overridepublic int code() {return code;}@Overridepublic String message() {return message;} } 该消息模型基于 ResultCode 来实现,代码如下 package com.xuecheng.framework.model.response;/ Created by mrt on 2018/3/5. 10000-- 通用错误代码 22000-- 媒资错误代码 23000-- 用户中心错误代码 24000-- cms错误代码 25000-- 文件系统/public interface ResultCode {//操作是否成功,true为成功,false操作失败boolean success();//操作代码int code();//提示信息String message(); 从 ResultCode 中我们可以看出,我们约定了用户中心的错误代码使用 23000,所以我们定义的一些错误信息的代码就从 23000 开始计数。 Service 在学习服务中定义 service 方法,此方法远程请求课程管理服务、媒资管理服务获取课程学习地址。 package com.xuecheng.learning.service.impl;import com.netflix.discovery.converters.Auto;import com.xuecheng.framework.domain.course.TeachplanMediaPub;import com.xuecheng.framework.domain.learning.response.GetMediaResult;import com.xuecheng.framework.exception.ExceptionCast;import com.xuecheng.framework.model.response.CommonCode;import com.xuecheng.learning.client.CourseSearchClient;import com.xuecheng.learning.service.LearningService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class LearningServiceImpl implements LearningService {@AutowiredCourseSearchClient courseSearchClient;/ 远程调用搜索服务获取已发布媒体信息中的url @param courseId 课程id @param teachplanId 媒体信息id @return/@Overridepublic GetMediaResult getMediaPlayUrl(String courseId, String teachplanId) {//校验学生权限,是否已付费等//远程调用搜索服务进行查询媒体信息TeachplanMediaPub mediaPub = courseSearchClient.getmedia(teachplanId);if(mediaPub == null) ExceptionCast.cast(CommonCode.FAIL);return new GetMediaResult(CommonCode.SUCCESS, mediaPub.getMediaUrl());} } Controller 调用 service 根据课程计划 id 查询视频播放地址: @RestController@RequestMapping("/learning/course")public class CourseLearningController implements CourseLearningControllerApi {@AutowiredLearningService learningService;@Override@GetMapping("/getmedia/{courseId}/{teachplanId}")public GetMediaResult getMediaPlayUrl(@PathVariable String courseId, @PathVariable String teachplanId) {//获取课程学习地址return learningService.getMedia(courseId, teachplanId);} } 测试 使用 swagger-ui 或postman 测试学习服务查询课程视频地址接口。 0x05 前端开发 需求分析 需要在学习中心前端页面需要完成如下功能: 1、进入课程学习页面需要带上 课程 Id参数及课程计划Id的参数,其中 课程 Id 参数必带,课程计划 Id 可以为空。 2、进入页面根据 课程 Id 取出该课程的课程计划显示在右侧。 3、进入页面后判断如果请求参数中有课程计划 Id 则播放该章节的视频。 4、进入页面后判断如果 课程计划id 为0则需要取出本课程第一个 课程计划的Id,并播放第一个课程计划的视频。 进入到模块 xc-ui-pc-leanring/src/module/course api方法 let sysConfig = require('@/../config/sysConfig')let apiUrl = sysConfig.xcApiUrlPre;/获取播放地址/export const get_media = (courseId,chapter) => {return http.requestGet(apiUrl+'/api/learning/course/getmedia/'+courseId+'/'+chapter);} 配置代理 在 Nginx 中的 ucenter.xuecheng.com 虚拟主机中配置 /api/learning/ 的路径转发,此url 请转发到学习服务。 学习服务upstream learning_server_pool{server 127.0.0.1:40600 weight=10;}学成网用户中心server {listen 80;server_name ucenter.xuecheng.com;个人中心location / {proxy_pass http://ucenter_server_pool;}后端搜索服务location /openapi/search/ {proxy_pass http://search_server_pool/search/; }学习服务location ^~ /api/learning/ {proxy_pass http://learning_server_pool/learning/;} } 视频播放页面 1、如果传入的课程计划id为0则取出第一个课程计划id 在 created 钩子方法中完成 created(){//当前请求的urlthis.url = window.location//课程idthis.courseId = this.$route.params.courseId//章节idthis.chapter = this.$route.params.chapter//查询课程信息systemApi.course_view(this.courseId).then((view_course)=>{if(!view_course || !view_course[this.courseId]){this.$message.error("获取课程信息失败,请重新进入此页面!")return ;}let courseInfo = view_course[this.courseId]console.log(courseInfo)this.coursename = courseInfo.nameif(courseInfo.teachplan){console.log("准备开始播放视频")let teachplan = JSON.parse(courseInfo.teachplan);this.teachplanList = teachplan.children;//开始学习if(this.chapter == "0" || !this.chapter){//取出第一个教学计划this.chapter = this.getFirstTeachplan();console.log("第一个教学计划id为 ",this.chapter);this.study(this.chapter);}else{this.study(this.chapter);} }})}, 取出第一个章节 id,用户未输入课程计划 id 或者输入为 0 时,播放第一个。 //取出第一个章节getFirstTeachplan(){for(var i=0;i<this.teachplanList.length;i++){let firstTeachplan = this.teachplanList[i];//如果当前children存在,则取出第一个返回if(firstTeachplan.children && firstTeachplan.children.length>0){let secondTeachplan = firstTeachplan.children[0];return secondTeachplan.id;} }return ;}, 开始学习: //开始学习study(chapter){// 获取播放地址courseApi.get_media(this.courseId,chapter).then((res)=>{if(res.success){let fileUrl = sysConfig.videoUrl + res.fileUrl//播放视频this.playvideo(fileUrl)}else if(res.message){this.$message.error(res.message)}else{this.$message.error("播放视频失败,请刷新页面重试")} }).catch(res=>{this.$message.error("播放视频失败,请刷新页面重试")});}, 2、点击右侧课程章节切换播放 在原有代码基础上添加 click 事件,点击调用开始学习方法(study)。 <li v‐if="teachplan_first.children!=null" v‐for="(teachplan_second, index) inteachplan_first.children"><i class="glyphicon glyphicon‐check"></i><a :href="url" @click="study(teachplan_second.id)">{ {teachplan_second.pname} }</a></li> 3、地址栏路由url变更 这里需要注意一个问题,在用户点击课程章节切换播放时,地址栏的 url 也应该同步改变为当前所选择的课程计划 id 4、在线学习按钮 将 learnstatus 默认更改为 1,这样就能显示出马上学习的按钮,方便我们后续的集成测试。 文件路径为 xc-ui-pc-static-portal/include/course_detail_dynamic.html 部分代码块如下 <script>var body= new Vue({ //创建一个Vue的实例el: "body", //挂载点是id="app"的地方data: {editLoading: false,title:'测试',courseId:'',charge:'',//203001免费,203002收费learnstatus: 1 ,//课程状态,1:马上学习,2:立即报名、3:立即购买course:{},companyId:'template',company_stat:[],course_stat:{"s601001":"","s601002":"","s601003":""} }, 简单的测试 访问在线学习页面:http://ucenter.xuecheng.com//learning/课程id/课程计划id 通过 url 传入两个参数:课程id 和 课程计划id 如果没有课程计划则传入0 测试项目如下: 1、传入正确的课程id、课程计划id,自动播放本章节的视频 2、传入正确的课程id、课程计划id传入0,自动播放第一个视频 3、传入错误的课程id 或 课程计划id,提示错误信息。 4、通过右侧章节目录切换章节及播放视频。 访问: http://ucenter.xuecheng.com//learning/4028e58161bcf7f40161bcf8b77c0000/4028e58161bd18ea0161bd1f73190008 传入正确的课程id、课程计划id,自动播放本章节的视频 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ef0xxym7-1595567273153)(https://qnoss.codeyee.com/20200704_15/image17)] 传入正确的课程id、课程计划id传入0,自动播放第一个视频 访问 http://ucenter.xuecheng.com//learning/4028e58161bcf7f40161bcf8b77c0000/0 识别出第一个课程计划的 id 需要注意的是这里的 chapter 参数是我自己在 study 函数里加上去的,可以忽略。 传入错误的课程id或课程计划id,提示错误信息。 通过右侧章节目录切换章节及播放视频。 点击章节即可播放,但是点击制定章节后 url 没有发生改变,这个问题暂时还没有解决,关注笔记后面的内容。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TOGdxwb4-1595567273158)(https://qnoss.codeyee.com/20200704_15/image20)] 完整的测试 准备工作 启动 RabbitMQ,启动 Logstash、ElasticSearch 建议把所有后端服务都开起来 启动 前端静态门户、启动 nginx 、启动课程管理前端 我们整理一下测试的流程 上传两个媒资视频文件,用于测试 进入到课程管理,为课程计划选择媒资信息 发布课程,等待 logstash 将数据采集到 ElasticSearch 的索引库中 进入学成网主页,点击课程,进入到搜索门户页面 搜索课程,进入到课程详情页面 点击开始学习,进入到课程学习页面,选择课程计划中的一个章节进行学习。 1、上传文件 首先我们使用之前开发的媒资管理模块,上传两个视频文件用于测试。 第一个文件上传成功 一些问题 在上传第二个文件时,发生了错误,我们来检查一下问题出在了哪里 在媒体服务的控制台中可以看到,在 mergeChunks 方法在校验文件 md5 时候抛出了异常 我们在 MD5 校验这里打个断点,重新上传文件,分析一下问题所在。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OpEMZGI8-1595567273166)(https://qnoss.codeyee.com/20200704_15/image23)] 单步调试后发现,合并文件后的MD5值与用户上传的源文件值不相等 方案1:删除本地分块文件重新尝试上传 考虑到可能是在用户上传完 视频的分块文件时发生了一些问题,导致合并文件后与源文件的大小不等,导致MD5也不相同,这里我们把这个视频上传到本地的文件全部删除,在媒资上传页面重新上传文件。 对比所有分块文件的字节大小和本地源文件的大小,完全是相等的 删除所有文件后重新上传,md5值还是不等,考虑从调试一下文件合并的代码。 方案2:检查前端提交的MD5值是否正确 在查阅是否有其他的MD5值获取方案时,发现了一个使用 windows 本地命令获取文件MD5值的方法 certutil -hashfile .\19-在线学习接口-集成测试.avi md5 惊奇的发现,TM的原来是前端那边转换的MD5值不正确,后端这边是没有问题的。 从前面的图可以看出,本地和后端转换的都是以一个 f6f0 开头的MD5值 那么问题就出现在前端了,还需要花一些时间去分析一下,这里暂时就先告一段落,因为上传了几个文件测试中只有这一个文件出现了问题。 2、为课程计划选择媒资信息 进入到一个课程的管理页面 http://localhost:12000//course/manage/baseinfo/4028e58161bcf7f40161bcf8b77c0000 将刚才我们上传的媒资文件的信息和课程计划绑定 选择效果如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-epKaqzCD-1595567273178)(https://qnoss.codeyee.com/20200704_15/image29)] 2、发布课程,等待 logstash 从 course_pub 以及 teachplan_media_pub 表中采集数据到 ElasticSearch 当中 发布成功后,我们可以从 teachplan_media_pub 表中看到刚才我们发布的媒资信息 再观察 Logstash 的控制台,发现两个 Logstash 的实例都对更新的课程发布信息进行了采集 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTUve2ik-1595567273183)(https://qnoss.codeyee.com/20200704_15/image32)] 3、前端门户测试 打开我们的门户主站 http://www.xuecheng.com/ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4wZe9R84-1595567273185)(https://qnoss.codeyee.com/20200704_15/image33)] 点击导航栏的课程,进入到我们的搜索门户页面 如果无法进入到搜索门户,请检查你的 xc-ui-pc-portal 前端工程是否已经启动 进入到搜索门户后,可以看到一些初始化时搜索的课程数据,默认是搜索第一页的数据,每页2个课程。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BJ1AKoJb-1595567273187)(https://qnoss.codeyee.com/20200704_15/image34)] 我们可以测试搜索一下前面我们选择媒资信息时所用的课程 点击课程,进入到课程详情页面,然后再点击开始学习。 点击马上学习后,会进入到该课程的在线学习页面,默认自动播放我们第一个课程计划中的视频。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcuLWnf2-1595567273193)(https://qnoss.codeyee.com/20200704_15/image37)] 我们可以在右侧的目录中选择第二个课程计划,会自动播放所选的课程计划所对应的媒资视频播放地址,该 播放地址正是我们刚才通过 Logstash 自动采集到 ElasticSearch 的索引信息,效果图如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cvi9Dr0Y-1595567273195)(https://qnoss.codeyee.com/20200704_15/image38)] 四、待完善的一些功能 课程发布前,校验课程计划里面是否包含二级课程计划 课程发布前,校验课程计划信息里面是否全部包含媒资信息 删除媒资信息,并且同步删除ES中的索引 在获取该课程的播放地址时校验用户的合法、 在线学习页面,点击右侧目录中的课程计划同时改变url中的课程计划地址 视频文件 19-在线学习接口-集成测试.avi 前端上传时提交的MD5值不正确 😁 认识作者 作者:👦 LCyee ,全干型代码🐕 自建博客:https://www.codeyee.com 记录学习以及项目开发过程中的笔记与心得,记录认知迭代的过程,分享想法与观点。 CSDN 博客:https://blog.csdn.net/codeyee 记录和分享一些开发过程中遇到的问题以及解决的思路。 欢迎加入微服务练习生的队伍,一起交流项目学习过程中的一些问题、分享学习心得等,不定期组织一起刷题、刷项目,共同见证成长。 本篇文章为转载内容。原文链接:https://blog.csdn.net/codeyee/article/details/107558901。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-12-16 12:41:01
73
转载
转载文章
...革新对于提升企业信息管理水平至关重要。这款基于现代浏览器技术和JSON数据交换标准的新型报表打印系统,无疑为企业信息化建设开辟了新的可能,也为行业标准的制定和完善提供了有力支撑。随着更多企业和机构开始重视数据驱动决策的重要性,类似这样的高效、易用且具有前瞻性的报表工具将迎来更为广阔的应用前景。
2023-04-01 18:34:12
234
转载
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
history | grep keyword
- 搜索包含关键词的历史命令。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"