首页 文章 精选 留言 我的

精选列表

搜索[初体验],共248篇文章
优秀的个人博客,低调大师

使用Rancher 2.3 启用Istio初体验

本文来自Rancher Labs Rancher的理念是Run Kubernetes Everywhere,Rancher 2.3中许多重大更新,让这一理念的实现又向前一步。 其中,最重要的两个特性是集成了Istio以及对Windows的支持。本文我们将主要讨论如何使用通过Rancher UI提供的Istio支持,并通过Kiali dashboard进行可视化。 前期准备: 正在运行的Kubernetes集群 安装Rancher并导入该集群 在本例中,我们将使用CIVO Cloud上的大型k3s托管集群,并且已经完成Rancher App的安装(在Civo Marketplace的Rancher应用程序将会在集群上安装Rancher,并将集群导入其中)。集群的设置可以参考以下步骤: https://medium.com/@SaiyamPathak/managed-k3s-is-it-a-thing-9397799c38a 启动集群之后,在集群创建过程中从marketplace选择Rancher进行安装。Civo将会启动Rancher server并导入集群。 集群准备就绪后,你将能看到Rancher 2.3的dashboard,它能够支持Istio和Kiali。让我们来探索一下这个dashboard吧! 集群创建之后,你可以下载kubeconfig,并连接集群。然后查看Rancher server以及cattle-agents是否起来并且运行。 kubectl get nodes NAME STATUS ROLES AGE VERSION kube-node-79ed Ready worker 96m v1.15.4-k3s.1 kube-master-bca5 Ready master 96m v1.15.4-k3s.1 kubectlg get pods -n cattle-system NAME READY STATUS RESTARTS AGE cattle-5669c57dcf-tw65t 1/1 Running 0 3h27m cattle-node-agent-8lppr 1/1 Running 0 3h27m cattle-node-agent-g5f6f 1/1 Running 0 3h27m cattle-cluster-agent-587b6d44cf-ppnjd 1/1 Running 0 3h27m 为了访问Rancher UI,创建一个ingress,rancher-ingress.yaml如下: >> kubectl apply -f rancher-ingress.yaml ingress.extensions/cattle-ingress created kubectl get ingress -n cattle-system NAME HOSTS ADDRESS PORTS AGE cattle-ingress * 172.31.0.189 80 32s 现在如果你访问任何节点ip,你都将看到Rancher server正在运行。 创建一个密码,保存URL。随后你应该能够看到导入的集群。 通过Rancher UI启用Istio 文档中是这样描述Istio的: 使用云平台的企业或组织可以从其中体会到很多益处。但是不可否认的是,采用云技术会对DevOps团队造成压力。开发人员必须使用微服务来构建可移植性,同时,运维人员管理超大型混合云和多云部署。而服务网格使得微服务更加易用,其中Istio可以帮助你连接、保护、控制和观察服务。 在很大程度上,Istio有助于降低部署的复杂性,并减轻开发团队的负担。它是一个完全开源的服务网格,可以在现有的分布式应用程序上透明地注入一层。同时,它也是一个平台,包括可将其集成到任何日志记录平台、遥测或策略系统中的各种API。Istio的多样功能可以让你能够成功、高效地运行分布式微服务架构,并提供统一地方式来保护、连接和监控微服务。 那么,现在我们开始通过Rancher UI中启用Istio,并部署吧。 要启用Istio,你需要访问UI上方的菜单栏,其路径是:工具> Istio。你可以更改许多配置选项。而现在,我想让所有配置都保持默认状态并将ingress网关设置为True。启用这一功能还将启用监控功能,这是Istio正常运行的先决条件。 启用之后,你将可以看到监控和Istio pod在命名空间cattle-prometheus(用于监控)和istio-system(用于istio)下出现。 >> kubectl get pods -n istio-system NAME READY STATUS RESTARTS AGE istio-citadel-6bb9c9f6fb-md9f8 1/1 Running 0 6m16s istio-tracing-64d646945-xm4sm 2/2 Running 0 6m15s istio-policy-68959c7999-5kmdb 2/2 Running 1 6m16s istio-galley-67848cd58-g5tbt 1/1 Running 0 6m16s kiali-5f8f876bd5-6djxf 2/2 Running 0 6m16s istio-telemetry-778bfdcf74-ps9vl 2/2 Running 1 6m16s istio-pilot-7546b9fdcc-rbxj8 2/2 Running 0 6m16s istio-ingressgateway-6f877dd689-rskn4 1/1 Running 0 6m16s istio-sidecar-injector-69c97ddbb5-x7jcv 1/1 Running 0 6m16s >> kubectl get pods -n cattle-prometheus NAME READY STATUS RESTARTS AGE prometheus-operator-monitoring-operator-79484b9c6f-zshlq 1/1 Running 0 7m42s exporter-node-cluster-monitoring-wnxtc 1/1 Running 0 7m39s exporter-node-cluster-monitoring-k68fb 1/1 Running 0 7m39s grafana-cluster-monitoring-5d676d89c5-vkbzm 2/2 Running 0 7m39s prometheus-cluster-monitoring-0 5/5 Running 1 7m15s exporter-kube-state-cluster-monitoring-5dfd658dc-pn8mt 1/1 Running 0 7m39s 现在,我们来进行Istio部署示例,生成流量并在Kiali dashboard中查看它。 我们将为示例应用程序创建deployment、Gateway以及虚拟服务,如下所示: kubectl label namespace default istio-injection=enabled namespace/default labeled kubectl apply -f service/details created serviceaccount/bookinfo-details created deployment.apps/details-v1 created service/ratings created serviceaccount/bookinfo-ratings created deployment.apps/ratings-v1 created service/reviews created serviceaccount/bookinfo-reviews created deployment.apps/reviews-v1 created deployment.apps/reviews-v2 created deployment.apps/reviews-v3 created service/productpage created serviceaccount/bookinfo-productpage created deployment.apps/productpage-v1 created kubectl apply -f gateway.networking.istio.io/bookinfo-gateway created kubectl apply -f virtualservice.networking.istio.io/bookinfo created 生成流量: 现在,应用程序已经部署,你可以通过Istio gateway查看它。 >> kubectl get pods NAME READY STATUS RESTARTS AGE details-v1-74f858558f-m5tsx 2/2 Running 0 10m ratings-v1-7855f5bcb9-lkhgg 2/2 Running 0 10m productpage-v1-8554d58bff-llnqh 2/2 Running 0 10m| reviews-v2-d6cfdb7d6-rl4zk 2/2 Running 0 10m reviews-v3-75699b5cfb-crdrd 2/2 Running 0 10m reviews-v1-59fd8b965b-rmct2 2/2 Running 0 10m >> kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 192.168.128.1 <none> 443/TCP 140m details ClusterIP 192.168.154.118 <none> 9080/TCP 10m ratings ClusterIP 192.168.207.69 <none> 9080/TCP 10m reviews ClusterIP 192.168.141.42 <none> 9080/TCP 10m productpage ClusterIP 192.168.128.87 <none> 9080/TCP 10m 点击Test用户和普通用户,来生成一些流量。 从UI上方的菜单栏中【资源】项,选择Istio。你可以看到以下图表: 点击屏幕上的Kiali图标。 Kiali Kiali是Istio的可视化控制台,它具有服务网格配置功能。它通过推断拓扑来帮助理解你的服务网格架构并提供你的网格的健康状态。此外,Kiali还提供了详细的指标,并且其集成了基本的Grafana,因此可用于高级查询。还集成了Jaeger,可提供分布式追踪。 您可以查看已部署应用程序的完整拓扑以及流程。 下面是已经部署的应用程序的图表: 以下是其他图表: 服务图表 版本化应用程序图 工作负载图 Jaeger 受Dapper和OpenZipkin的启发,Jaeger被设计为一个开源分布式跟踪系统,由Uber Technologies发布。它用于监控、诊断基于微服务的分布式系统,包括: 分布式上下文传播(Distributed Context Propagation) 分布式事务监控 根源分析(Root cause analysis) 服务依赖分析 性能/延迟优化 Jaeger UI Jaeger 查询 总 结 在本文中,我们讨论了在Rancher 2.3.x中如何安装Istio并使用Kiali可视化服务网格。我们还部署了一个示例应用程序并生成了一些流量,还使用Kiali和Jaeger查看它们。 如果你还想了解更多关于Istio、Kiali以及Jaeger的内容,欢迎访问以下网站观看视频: https://space.bilibili.com/430496045

优秀的个人博客,低调大师

阳光启明:文化产业上云初体验

本文正在参加“最佳上云实践”评选,来给我们投票吧:https://yq.aliyun.com/activity/158(编号36) 北京阳光启明科技文化有限公司是一家以计算机软件(信息技术)开发、销售自主产品、售后服务以及相关技术咨询的电子信息技术企业。作为国内首屈一指的顶级集成商之一,公司一直致力于协助国家推动公共文化服务体系建设,为各区域搭建数字服务平台、提供整体解决方案、研发特色文化产品。主要文化产品有公共文化数字服务云平台、公共文化一体机、扶贫资源宝等,目前已累计为全国各地区发放各类文化产品达10,000多台。 上云后取得显著成效 虽然我司目前使用阿里云还不足两年,但是效果非常显著。自从租用阿里云服务器后,无需手动装载服务器系统,只需勾选相应系统程序选项,即可予以自动装载服务器程序,操作上省时省力,性能上安全可靠。尤其在数据的可配处理以及安全稳定上,阿里云性能卓越,为我司开展线上活动提供了强有力的支撑,下面可通过具体数据来分析: 图1 各项目云平台使用记录 图2 项目具体数据 通过上图可看出阿里云的几个显著特点: 1. 带宽可配: 带宽资源稳定可靠、绝对独享,有效解决带宽瓶颈,消除传统IDC带宽资源争抢的烦恼。 2. 数据信息安全: 采用自定义防火墙和安全组隔离技术, 有效杜绝IP/MAC欺骗和ARP攻击。免费启用云盾后可进行端口入侵扫描、挂马扫描、漏洞扫描等,可以有效抗击来自互联网的DDoS及其他安全攻击,保障业务的持续运行。 3. 网络稳定: 绿色节能机房,BGP多线(中国电信、联通、移动、教育网等)接入,确保全国用户访问畅通。 带宽资源稳定可靠、绝对独享,有效解决带宽瓶颈,消除传统IDC带宽资源争抢的烦恼。 上云实践总结 我司通过亲身实践,深刻感受到上云后为给公司带来的效益,具体可总结为以下几点: 1、节约成本 企业想要在自己的服务器上进行数据存储,就必须购买高昂的硬件和软件,而且后期还需要花费大量的人力物力来维护这些软件。但是通过云存储,服务器商可以服务成千上万的企业,并可以划分不同消费群体服务,来帮助初创公司减少不必要的成本预算。 2、更好的备份本地数据并可以异地处理日常数据 云存储可以为企业提供重要数据备份和保护,即使遇到突发问题或在恶劣条件下,依然能保持正常工作。比如,企业办公场所发生自然灾害,如果数据是异地存储,那么数据依然存在,它是非常安全的。如果问题出现在企业办公室,那么你随便去一个安全的地方通过笔记本就可以继续访问数据和更新数据。 3、更多的访问和更好的竞争 对于于中小企业来说,由于自身条件和环境的限制,不能够花高昂的费用来打造好的管理系统。在同等商业机遇面前,相比可以花重金打造数据存储中心的大企业,中小企业完全没有实力与之竞争。而通过云存储,中小企业不需要多少费用就可以争取和大企业同等的商业机遇。 附:株洲星级评定活动真实案例 星级评定活动是我司为株洲市举办的一次全市文艺团队大联欢,共计93支团队参与活动,活动时间为2016年12月11日至2016年12月25日。下面通过活动期间的详细数据来看阿里云在活动中所起的关键性作用。 图3 活动详细数据 图4 活动数据折线展示 活动期间总访问量高达5,350,000次,通过图3、图4可以看出活动每天的实时数据变化非常大,即使是在这种数据激增、访问大量并发的情况下,阿里云依然能够依靠自身系统及时调整带宽,加大并发流量端口,保持整体网络的稳定有序,保证群众正常访问数据、浏览活动实时信息。最终活动取得圆满成功。

优秀的个人博客,低调大师

PaddleOCR初体验,基于PaddleHub Serving的服务部署

PaddleOCR提供2种服务部署方式: 基于PaddleHub Serving的部署:代码路径为"./deploy/hubserving",按照本教程使用; 基于PaddleServing的部署:代码路径为"./deploy/pdserving",使用方法参考文档。 基于PaddleHub Serving的服务部署 hubserving服务部署目录下包括检测、识别、2阶段串联三种服务包,请根据需求选择相应的服务包进行安装和启动。目录结构如下: deploy/hubserving/ └─ ocr_det 检测模块服务包 └─ ocr_rec 识别模块服务包 └─ ocr_system 检测+识别串联服务包 每个服务包下包含3个文件。以2阶段串联服务包为例,目录如下: deploy/hubserving/ocr_system/ └─ __init__.py 空文件,必选 └─ config.json 配置文件,可选,使用配置启动服务时作为参数传入 └─ module.py 主模块,必选,包含服务的完整逻辑 └─ params.py 参数文件,必选,包含模型路径、前后处理参数等参数 快速启动服务 以下步骤以检测+识别2阶段串联服务为例,如果只需要检测服务或识别服务,替换相应文件路径即可。 1. 准备环境 克隆代码:https://gitee.com/paddlepaddle/PaddleOCR.git,解压并进入PaddleOCR文件夹 # 安装paddlehub pip3 install paddlehub --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple 2. 下载推理模型 PaddleOCR下新建‘inference’文件夹,准备推理模型并放到‘inference’文件夹里面,默认使用的是v1.1版的超轻量模型, https://github.com/PaddlePaddle/PaddleOCR/blob/develop/doc/doc_ch/quickstart.md 默认模型路径为: 检测模型:./inference/ch_ppocr_mobile_v1.1_det_infer/ 识别模型:./inference/ch_ppocr_mobile_v1.1_rec_infer/ 方向分类器:./inference/ch_ppocr_mobile_v1.1_cls_infer/ 模型路径可在params.py中查看和修改。更多模型可以从PaddleOCR提供的模型库下载,也可以替换成自己训练转换好的模型。 3. 安装服务模块 PaddleOCR提供3种服务模块,根据需要安装所需模块。 在Linux环境下,安装示例如下: # 安装检测服务模块: hub install deploy/hubserving/ocr_det/ # 或,安装识别服务模块: hub install deploy/hubserving/ocr_rec/ # 或,安装检测+识别串联服务模块: hub install deploy/hubserving/ocr_system/ 在Windows环境下(文件夹的分隔符为\),安装示例如下: # 安装检测服务模块: hub install deploy\hubserving\ocr_det\ # 或,安装识别服务模块: hub install deploy\hubserving\ocr_rec\ # 或,安装检测+识别串联服务模块: hub install deploy\hubserving\ocr_system\ 4. 启动服务 方式1. 命令行命令启动(仅支持CPU) 启动命令: hub serving start -c D:\XHX\Develop\Paddale\PaddleOCR\deploy\hubserving\ocr_system\config.json 注意:如果启动报错xxx路径找不到,去PaddleOCR\deploy\hubserving下的ocr_system、ocr_det、ocr_rec的params.py文件,将所有的model_dir 替换为符合win格式的绝对路径即可; 参数: 参数 用途 --modules/-m PaddleHub Serving预安装模型,以多个Module==Version键值对的形式列出 当不指定Version时,默认选择最新版本 --port/-p 服务端口,默认为8866 --use_multiprocess 是否启用并发方式,默认为单进程方式,推荐多核CPU机器使用此方式 Windows操作系统只支持单进程方式 --workers 在并发方式下指定的并发任务数,默认为2*cpu_count-1,其中cpu_count为CPU核数 如启动串联服务:hub serving start -m ocr_system 这样就完成了一个服务化API的部署,使用默认端口号8866。 方式2. 配置文件启动(支持CPU、GPU) 启动命令: hub serving start -c config.json 其中,config.json格式如下: { "modules_info": { "ocr_system": { "init_args": { "version": "1.0.0", "use_gpu": true }, "predict_args": { } } }, "port": 8868, "use_multiprocess": false, "workers": 2 } init_args中的可配参数与module.py中的_initialize函数接口一致。其中,当use_gpu为true时,表示使用GPU启动服务。 predict_args中的可配参数与module.py中的predict函数接口一致。 注意: 使用配置文件启动服务时,其他参数会被忽略。 如果使用GPU预测(即,use_gpu置为true),则需要在启动服务之前,设置CUDA_VISIBLE_DEVICES环境变量,如:export CUDA_VISIBLE_DEVICES=0,否则不用设置。 use_gpu不可与use_multiprocess同时为true。 如,使用GPU 3号卡启动串联服务: export CUDA_VISIBLE_DEVICES=3 hub serving start -c deploy/hubserving/ocr_system/config.json 发送预测请求 配置好服务端,可使用以下命令发送预测请求,获取预测结果: python tools/test_hubserving.py server_url image_path 需要给脚本传递2个参数: server_url:服务地址,格式为 http://[ip_address]:[port]/predict/[module_name] 例如,如果使用配置文件启动检测、识别、检测+识别2阶段服务,那么发送请求的url将分别是: http://127.0.0.1:8866/predict/ocr_det http://127.0.0.1:8867/predict/ocr_rec http://127.0.0.1:8868/predict/ocr_system image_path:测试图像路径,可以是单张图片路径,也可以是图像集合目录路径 访问示例: python tools/test_hubserving.py http://127.0.0.1:8868/predict/ocr_system ./doc/imgs/ 返回结果格式说明 返回结果为列表(list),列表中的每一项为词典(dict),词典一共可能包含3种字段,信息如下: 字段名称 数据类型 意义 text str 文本内容 confidence float 文本识别置信度 text_region list 文本位置坐标 不同模块返回的字段不同,如,文本识别服务模块返回结果不含text_region字段,具体信息如下: 字段名/模块名 ocr_det ocr_rec ocr_system text ✔ ✔ confidence ✔ ✔ text_region ✔ ✔ 说明:如果需要增加、删除、修改返回字段,可在相应模块的module.py文件中进行修改,完整流程参考下一节自定义修改服务模块。 自定义修改服务模块 如果需要修改服务逻辑,你一般需要操作以下步骤(以修改ocr_system为例): 1、 停止服务 hub serving stop --port/-p XXXX 2、 到相应的module.py和params.py等文件中根据实际需求修改代码。 例如,如果需要替换部署服务所用模型,则需要到params.py中修改模型路径参数det_model_dir和rec_model_dir,如果需要关闭文本方向分类器,则将参数use_angle_cls置为False,当然,同时可能还需要修改其他相关参数,请根据实际情况修改调试。强烈建议修改后先直接运行module.py调试,能正确运行预测后再启动服务测试。 3、 卸载旧服务包 hub uninstall ocr_system 4、 安装修改后的新服务包 hub install deploy/hubserving/ocr_system/ 5、重新启动服务 hub serving start -m ocr_system

优秀的个人博客,低调大师

使用Python开发鸿蒙设备程序(0-初体验

到目前为止,鸿蒙设备开发的“官方指定语言”还是C语言! 这看起来是一件正常的事,毕竟鸿蒙设备开发还是属于嵌入式开发的范畴,而在嵌入式开发中C语言又是当之无愧的首选,所以,大家也都接受了这个现实。。。。。。。 上周末,有幸能和华为的大佬们进行面对面交流(其实我是去抱大腿的),我们都一致认为:如果设备开发能支持更简洁的开发方式(如:简单的语言,简单的开发环境),相信会有更多的开发者加入。。。 那么现在,有没有一种语言,受众面很广又简单易学呢? 当然是有的,相信你已经知道了,就是 Python ! 这几年 Python 借助 AI 的兴起而进入大众视野,她的简单易学深受欢迎,很多小学生都能够用她来编程了。 所以,一个看起来很疯狂的想法从我大脑蹦了出来:如果鸿蒙设备开发可以用 Python ,那么肯定能降低学习门槛,吸引更多的开发者。。。 于是,说干就干。。。 我先调研了目前的各种Python实现(Python是开源的,可以通过源码了解实现),发现公版 Python 和 MicroPython 都可以是我的起点(baseline),毕竟我没有必要照着 Python 规范写一个解释器出来!然而,困难还是有的,这两种实现都有非常多的依赖,而且设计目标又分别不同:公版 Python 是一个大而全的系统(解释器,库,等等),目标是只要资源丰富爱怎么玩就怎么玩;MicroPython 从公版 Python 剪裁而来,并做了扩展,基本定义成了一个微型嵌入式设备上的操作系统。 那么,怎么开始呢,做选择真的很难啊!!!! 我开始整理思路,我想干什么?!之后有了下面的架构图。 很显然,我想的是提供鸿蒙设备开发的其它方式,而不是用 Python 替换 C 语言!所以,我的第一个里程碑(milestone)是获得一个可以在鸿蒙设备(Hi3861)上运行的 Python 解释器!有了这个 Python 解释器,接下来就是设计 Python 版的鸿蒙系统开发接口了。。。 确定了目标之后,接下来的问题就是:究竟是用公版 Python 开刀还是用 MicroPython 开刀? 通过两者代码的对比阅读,我发现 MicroPython 居然在解释器部分对公版 Python 也做了剪裁。。。这,为什么啊??? 我暂时也没有确切答案,不过从代码实现可以看出 MicroPython 是为了适配更多低配置的硬件而做了取舍!那显然,很多东西人家都考虑过了!我想着目前鸿蒙设备的定位也是低配硬件,那么用 MicroPython 开刀看起来更合适。 =========== 华丽的分割线 Begin =========== 通过 2 天的手术的改造,能够运行的版本(DTPython)就有了!! 使用方式如下: 下载文末附件中的 libdtpython.a,并将其拷贝到 \code-1.0\vendor\hisi\hi3861\hi3861\build\libs 目录下,如图: 【可选】编写 Python 代码(目前未提供任何库支持),并使用文末附件中的 Txt2Str 工具将其转换为 C 字符串,用法如下: 说明: Txt2Str 将 Python 代码用 C 字符串的形式存储到 C 文件中(如上图中的 test.c) 将转换得到的 C 文件加入工程中即可在 C 代码中使用 Python 代码(可参考文末附件中的示例) 注意:目前还没有简单的方法直接将 Python 源文件烧写到设备中,因此才需要上述步骤。 在设备开发中使用 Python 代码,示例如下: 运行结果如下: =========== 华丽的分割线 End =========== 后记: 目前仅仅能够运行基本的 Python 代码,大概率还存在很多需要解决的问题。 希望大家都来试玩我的这个方案,多找Bug,多提Issue。 现阶段的代码乱一坨,实在不好意思拿出来丢人现眼,基本功能稍微稳定些,直接开源!!! Enjoy It ! 获取原文资源包 作者:唐佐林 想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区 https://harmonyos.51cto.com

优秀的个人博客,低调大师

阿里云VMware无代理整机迁移服务初体验

当前对于很多企业客户,期待着把线下机房(服务器)搬迁上云,而传统的文件级别的迁云工具在虚拟化环境中操作又比较复杂,因此一款更加专业便捷的迁云服务便应运而生——阿里云VMware无代理整机迁移服务。 VMware整机迁移服务主要提供针对VMware环境的虚拟机提供非侵入式的无代理整机迁移功能,主要原理是基于VMware的快照以及磁盘级别的数据读取功能,将全盘的数据在ECS上云盘上进行重放。下面来试用一下这款服务,看看体验如何。 创建迁移网关: 首先,登录混合云备份(HBR)控制台,进入虚机迁移的页面,点击创建迁移网关: 输入网关的名字后即可完成创建,接下来点击下载客户端,会将客户端镜像Alibaba-Cloud-Migration-Appliance-*.ova下载到本地 部署网关: 登录本地的vCenter环境,部署OVA镜像(目前只支持通过vCenter的Web Client部署,并且未来VMware也只会支持Web Client,而不支持Native Client了): 激活网关: 网关开机后,访问http://[网关ip地址]:8011,进入激活页面后输入ak,以及上传证书(控制台下载网关处,或者在网关的操作中) 激活成功后,点击确定,会跳转到迁移的控制台: 添加vCenter: 在迁移控制台上,点击“查看”->“添加vCenter服务器”: 输入vCenter的地址,用户名和密码后,即可完成添加 进行迁移: 点击“迁移”按钮: 输入迁移的名称,并选择是否立即迁移,或者进行计划迁移: 然后在类似vCenter浏览目录的页面选择需要进行迁移的虚拟机: 最后便是选择迁移到ECS的配置: 全部配置完成后,点击创建,即可启动当前这个迁移任务。在迁移状态页面,我们便可以看到它: 确认迁移结果: 等迁移任务结束时,我们可以通过ECS的链接进入到ECS的详情页,登录并检查机器。 整体试用下来,VMware迁移服务在易用性上表现良好,并且不需要侵入待迁移服务器的系统内部,安全性有保障,如果有需要将VMware环境虚拟机迁移到阿里云的场景,用它是非常合适的。

优秀的个人博客,低调大师

初体验-阿里云短视频 SDK For Android 快速接入

前言 近期的一些创意短视频 App 风靡年轻群体,比较典型的例如抖音、MUSE 等,阿里云也适时地推出了简单易用的短视频 SDK,帮助开发者们以较低的成本快速引入功能完备的创意短视频功能。本文主要介绍如何快速接入阿里云短视频 SDK 的三个版本(基础版、标准版和专业版)。帮助开发者以最快的速度了解接入的基本方式。 本文描述的阿里云短视频 SDK 版本基于 3.4.0,后续升级接口变动请参考 阿里云短视频 SDK 文档。示例工程代码为 Kotlin,Java 接入类似。 正文 由于三个版本接入方式大同小异,本文将着重介绍基础版接入过程,标准版和专业版可以基于基础版方式接入,后续仅说明接入差异的地方。 基础版接入 1. 引入 aar 以及 so 目前 aar 平台版本最低要求 >= 4.3,先从SDK 下载页面下载相应版本的 SDK,解压之后,将 libs 文件夹下的 QuSdk-RC.aar 拷到 Android 工程模块中的 libs 文件夹下,将 jniLibs 文件夹下的 armeabi-v7a 文件夹也整体拷贝到 libs 文件夹下。拷贝完成之后目录的文件如下: 之后按照如下方式修改 Android 项目工程主模块下的 build.gradle 文件: Step1. 修改 jniLibs 的源文件夹; android { sourceSets.main { jniLibs.srcDir "libs" } } Step2. 将 libs 文件夹加入仓库中; repositories { flatDir { dirs 'libs' } } Step3. 增加 aar 所需依赖。 dependencies { implementation(name: 'QuSdk-RC', ext: 'aar') implementation 'com.android.support:appcompat-v7:24.2.1' implementation 'com.android.support:design:24.2.1' implementation 'com.google.code.findbugs:jsr305:3.0.0' implementation 'com.github.bumptech.glide:glide:3.7.0' implementation 'pub.devrel:easypermissions:0.2.1' implementation 'com.squareup.okhttp3:okhttp:3.2.0' implementation 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar' implementation 'com.squareup.okio:okio:1.12.0' implementation 'com.google.code.gson:gson:2.8.0' } 如果此处遭遇 java.lang.NoSuchFieldError 错误,可以参考短视频安卓常见问题解决。 2. 初始化 SDK 请根据具体的项目情况选择合适的 SDK 初始化时机,Demo 工程在 Applicatioin 的 onCreate() 方法中初始化。 package me.bogerchan.alishortvideodemo import android.app.Application import com.aliyun.common.httpfinal.QupaiHttpFinal /** * Created by hb.chen on 2018/1/6. */ class MyApplication : Application() { override fun onCreate() { super.onCreate() System.loadLibrary("QuCore-ThirdParty") System.loadLibrary("QuCore") QupaiHttpFinal.getInstance().initOkHttpFinal() } } 3. 开始书写你的业务逻辑 经过上述过程,实际上已经接入完成,这时候你可以参考文档直接开始使用各种 API 了,附下示例代码。 package me.bogerchan.alishortvideodemo import android.Manifest import android.app.Activity import android.content.Intent import android.content.pm.PackageManager import android.os.Bundle import android.support.v4.app.ActivityCompat import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.aliyun.demo.recorder.AliyunVideoRecorder import com.aliyun.struct.common.VideoQuality import com.aliyun.struct.snap.AliyunSnapVideoParam import me.bogerchan.alishortvideodemo.basic.R class MainActivity : AppCompatActivity() { companion object { val REQUEST_CODE_RECORD_VIDEO = 1 val REQUEST_CODE_FOR_PERMISSION = 2 } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) findViewById(R.id.btn_start_record).setOnClickListener { startRecordActivity() } ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), REQUEST_CODE_FOR_PERMISSION) } private fun startRecordActivity() { val recordParam = AliyunSnapVideoParam.Builder() .setResolutionMode(AliyunSnapVideoParam.RESOLUTION_720P) .setRatioMode(AliyunSnapVideoParam.RATIO_MODE_9_16) .setRecordMode(AliyunSnapVideoParam.RECORD_MODE_AUTO) .setNeedClip(true) .setMaxDuration(10000) .setMinDuration(2000) .setVideQuality(VideoQuality.HD) .setSortMode(AliyunSnapVideoParam.SORT_MODE_MERGE) .build() AliyunVideoRecorder.startRecordForResult(this, REQUEST_CODE_RECORD_VIDEO, recordParam) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { REQUEST_CODE_RECORD_VIDEO -> { if (resultCode == Activity.RESULT_OK && data != null) { val type = data.getIntExtra(AliyunVideoRecorder.RESULT_TYPE, 0) if (type == AliyunVideoRecorder.RESULT_TYPE_CROP) { Toast.makeText(this, "类型为裁剪", Toast.LENGTH_SHORT).show() } else if (type == AliyunVideoRecorder.RESULT_TYPE_RECORD) { Toast.makeText(this, "文件路径为 " + data.getStringExtra(AliyunVideoRecorder.OUTPUT_PATH), Toast.LENGTH_SHORT).show() } } else if (resultCode == Activity.RESULT_CANCELED) { Toast.makeText(this, "用户取消录制", Toast.LENGTH_SHORT).show() } } } } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE_FOR_PERMISSION -> { grantResults.forEach { if (it == PackageManager.PERMISSION_DENIED) { Toast.makeText(this, "没有权限,不玩了", Toast.LENGTH_SHORT).show() finish() return@forEach } } } } } } 标准版接入 1. 引入 aar 以及 so 标准版相较于基础版,在引入 so 文件时候多了几个文件,同时 aar 文件名有所变动。最终拷贝结果如下:build.gradle 文件修改与基础版接入一样,只是需要将接入 aar 文件名替换成标准版对应的名字。 2. 初始化 SDK 相较于基础版,需要加载的 so 增多了几个,其中部分 so 文件作为可选功能根据实际情况决定是否加载,具体可以参阅阿里云短视频 SDK 文档。接入后的 Application 文件参考: package me.bogerchan.alishortvideodemo import android.app.Application import com.aliyun.common.httpfinal.QupaiHttpFinal /** * Created by hb.chen on 2018/1/6. */ class MyApplication : Application() { override fun onCreate() { super.onCreate() System.loadLibrary("aliresample") System.loadLibrary("live-openh264") System.loadLibrary("QuCore-ThirdParty") System.loadLibrary("QuCore") QupaiHttpFinal.getInstance().initOkHttpFinal() } } 3. 开始书写你的业务逻辑 经过上述过程,实际上已经接入完成,这时候你可以参考文档直接开始使用各种 API 了,附下示例代码。 package me.bogerchan.alishortvideodemo import android.Manifest import android.content.pm.PackageManager import android.opengl.GLSurfaceView import android.os.Bundle import android.support.v4.app.ActivityCompat import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.aliyun.recorder.AliyunRecorderCreator import com.aliyun.struct.recorder.CameraType import com.aliyun.struct.recorder.MediaInfo import me.bogerchan.alishortvideodemo.std.R class MainActivity : AppCompatActivity() { companion object { val REQUEST_CODE_FOR_PERMISSION = 1 } private val mRecorder by lazy { AliyunRecorderCreator.getRecorderInstance(this) } private var mCameraType = CameraType.FRONT override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), REQUEST_CODE_FOR_PERMISSION) initAliyunRecorder() findViewById(R.id.btn_start_record).setOnClickListener { Toast.makeText(this, "开始录制片段", Toast.LENGTH_SHORT).show() mRecorder.startRecording() } findViewById(R.id.btn_stop_record).setOnClickListener { Toast.makeText(this, "停止录制片段", Toast.LENGTH_SHORT).show() mRecorder.stopRecording() } findViewById(R.id.btn_finish_record).setOnClickListener { Toast.makeText(this, "结束录制", Toast.LENGTH_SHORT).show() mRecorder.finishRecording() } findViewById(R.id.btn_change_camera_type).setOnClickListener { Toast.makeText(this, "切换前后置", Toast.LENGTH_SHORT).show() mRecorder.switchCamera() } } override fun onStart() { super.onStart() mRecorder.startPreview() } override fun onPause() { super.onPause() mRecorder.stopPreview() } override fun onDestroy() { super.onDestroy() AliyunRecorderCreator.destroyRecorderInstance() } private fun initAliyunRecorder() { mRecorder.setDisplayView(findViewById(R.id.glsv_content) as GLSurfaceView) val mediaInfo = MediaInfo() mediaInfo.videoWidth = 800 mediaInfo.videoHeight = 1200 mediaInfo.isHWAutoSize = true mRecorder.setMediaInfo(mediaInfo) mRecorder.setCamera(mCameraType) mRecorder.setOutputPath(externalCacheDir.absolutePath + "/capture.mp4") } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE_FOR_PERMISSION -> { grantResults.forEach { if (it == PackageManager.PERMISSION_DENIED) { Toast.makeText(this, "没有权限,不玩了", Toast.LENGTH_SHORT).show() finish() return@forEach } } } } } } 专业版接入 1. 引入 aar 以及 so 专业版相较于基础版,在引入 so 文件时候多了几个文件,同时 aar 文件名有所变动。最终拷贝结果如下: build.gradle 文件修改与基础版接入一样,只是需要将接入 aar 文件名替换成专业版对应的名字。 2. 初始化 SDK 相较于基础版,需要加载的 so 增多了几个,其中部分 so 文件作为可选功能根据实际情况决定是否加载,具体可以参阅阿里云短视频 SDK 文档。接入后的 Application 文件参考: package me.bogerchan.alishortvideodemo import android.app.Application import com.aliyun.common.httpfinal.QupaiHttpFinal /** * Created by hb.chen on 2018/1/6. */ class MyApplication : Application() { override fun onCreate() { super.onCreate() System.loadLibrary("live-openh264") System.loadLibrary("QuCore-ThirdParty") System.loadLibrary("QuCore") System.loadLibrary("FaceAREngine") System.loadLibrary("AliFaceAREngine") QupaiHttpFinal.getInstance().initOkHttpFinal() } } 3. 开始书写你的业务逻辑 经过上述过程,实际上已经接入完成,这时候你可以参考文档直接开始使用各种 API 了,附下示例代码。 package me.bogerchan.alishortvideodemo import android.Manifest import android.content.pm.PackageManager import android.opengl.GLSurfaceView import android.os.Bundle import android.support.v4.app.ActivityCompat import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.aliyun.recorder.AliyunRecorderCreator import com.aliyun.struct.recorder.CameraType import com.aliyun.struct.recorder.MediaInfo import me.bogerchan.alishortvideodemo.pro.R class MainActivity : AppCompatActivity() { companion object { val REQUEST_CODE_FOR_PERMISSION = 1 } private val mRecorder by lazy { AliyunRecorderCreator.getRecorderInstance(this) } private var mCameraType = CameraType.FRONT override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), REQUEST_CODE_FOR_PERMISSION) initAliyunRecorder() findViewById(R.id.btn_start_record).setOnClickListener { Toast.makeText(this, "开始录制片段", Toast.LENGTH_SHORT).show() mRecorder.startRecording() } findViewById(R.id.btn_stop_record).setOnClickListener { Toast.makeText(this, "停止录制片段", Toast.LENGTH_SHORT).show() mRecorder.stopRecording() } findViewById(R.id.btn_finish_record).setOnClickListener { Toast.makeText(this, "结束录制", Toast.LENGTH_SHORT).show() mRecorder.finishRecording() } findViewById(R.id.btn_change_camera_type).setOnClickListener { Toast.makeText(this, "切换前后置", Toast.LENGTH_SHORT).show() mRecorder.switchCamera() } } override fun onStart() { super.onStart() mRecorder.startPreview() } override fun onPause() { super.onPause() mRecorder.stopPreview() } override fun onDestroy() { super.onDestroy() AliyunRecorderCreator.destroyRecorderInstance() } private fun initAliyunRecorder() { mRecorder.setDisplayView(findViewById(R.id.glsv_content) as GLSurfaceView) val mediaInfo = MediaInfo() mediaInfo.videoWidth = 800 mediaInfo.videoHeight = 1200 mediaInfo.isHWAutoSize = true mRecorder.setMediaInfo(mediaInfo) mRecorder.setCamera(mCameraType) mRecorder.needFaceTrackInternal(true) mRecorder.setOutputPath(externalCacheDir.absolutePath + "/capture.mp4") } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_CODE_FOR_PERMISSION -> { grantResults.forEach { if (it == PackageManager.PERMISSION_DENIED) { Toast.makeText(this, "没有权限,不玩了", Toast.LENGTH_SHORT).show() finish() return@forEach } } } } } } 结语 至此已经介绍完了阿里云短视频 SDK 的接入方法,示例代码展示的仅仅只是阿里云视频 SDK 强大功能的冰山一角,开发者们可以通过相关的 SDK 文档获取更多的接口信息。如果集成过程中遇到问题,在联系客服之前不妨先看下 常见问题解决,说不定你的问题就在里面。

优秀的个人博客,低调大师

ASP.NET MVC 3 Beta初体验之WebGrid

ASP.NET MVC 3 Beta中除了推出一种新的视图引擎Razor。还推出了几种新的HtmlHelper。我比较关注的是WebGrid,这篇文章将介绍一下WebGrid的使用。WebGrid提供了分页和排序的功能,在此之前在MVC中分页和排序时需要自己去写的。这篇文章将分别介绍在aspx视图引擎和Razor视图引擎中如何使用它。 我通过ADO.NET Entity Data Model从NORTHWND的Products中表中取数据。在Controller中取数据: public class HomeController : Controller { public ActionResult Index() { NORTHWNDEntities entity = new NORTHWNDEntities(); return View( entity.Products.ToList()); } } 在aspx视图引擎中使用WebGrid代码如下: < div id ="grid" > <% var grid = new WebGrid(source: Model, defaultSort: " ProductName " , rowsPerPage: 5 ); %> <% = grid.GetHtml( tableStyle: " grid " , headerStyle: " head " , alternatingRowStyle: " alt " , columns: grid.Columns( grid.Column(format: (item) => Html.ActionLink( " Edit " , " Edit " , new { id = item.ProductID })), grid.Column(format: (item) => Html.ActionLink( " Delete " , " Delete " , null , new { onclick = string .Format( " deleteRecord('Employee', '{0}') " , item.ProductID), @class = " Delete " , href = " JavaScript:void(0) " })), grid.Column( " ProductName " , " 产品名称 " ), grid.Column( " QuantityPerUnit " , " 每单位数量 " ), grid.Column( " UnitPrice " , " 单价 " ), grid.Column( " UnitsInStock " , " 库存单位 " ), grid.Column( " UnitsOnOrder " , " 订单单位 " ), grid.Column( " ReorderLevel " , " 重新排序级别 " ), grid.Column( " Discontinued " , " 已停产 " ) ) ) %> </ div > 在Razor中使用WebGrid代码如下: < div id ="grid" > @{ var grid = new WebGrid(source: Model, defaultSort: "ProductName", rowsPerPage: 10); } @grid.GetHtml( tableStyle: "grid", headerStyle: "head", alternatingRowStyle: "alt", columns: grid.Columns( grid.Column(format: (item) => Html.ActionLink("Edit", "Edit", new { id = item.ProductID })), grid.Column(format: (item) => Html.ActionLink("Delete", "Delete", null, new { onclick = string.Format("deleteRecord('Product', '{0}')", item.ProductID), @class = "Delete", href = "JavaScript:void(0)" })), grid.Column("ProductName","产品名称"), grid.Column("QuantityPerUnit","每单位数量"), grid.Column("UnitPrice","单价"), grid.Column("UnitsInStock", "库存单位"), grid.Column("UnitsOnOrder","订单单位"), grid.Column("ReorderLevel","重新排序级别"), grid.Column("Discontinued","已停产") ) ) </ div > WebGrid构造函数如下: public WebGrid(IEnumerable<dynamic> source, IEnumerable<string> columnNames = null, string defaultSort = null, int rowsPerPage = 10, bool canPage = true, bool canSort = true, string ajaxUpdateContainerId = null, string fieldNamePrefix = null, string pageFieldName = null, string selectionFieldName = null, string sortFieldName = null, string sortDirectionFieldName = null); 常见参数意思是: 1、source 表示数据源 2、columnNames表示显示的列 3、defaultSort 默认按什么排序 4、rowsPerPage 每页多少行数据 5、canPage 是否能排序 上面两段代码的意思是定义了一个既分页又能排序的grid。 运行: 在看看两个view的完整代码: aspx: <% @ Page Title = "" Language = " C# " MasterPageFile = " ~/Views/Shared/Site.Master " Inherits = " System.Web.Mvc.ViewPage<List<WebGridAspx.Models.Products>> " %> < asp:Content ID = " Content1 " ContentPlaceHolderID = " TitleContent " runat = " server " > 产品列表 </ asp:Content > < asp:Content ID = " Content2 " ContentPlaceHolderID = " MainContent " runat = " server " > < script type = " text/javascript " > function deleteRecord(a, b) { alert( " 删除: " + b); } </ script > < h2 > 产品列表 </ h2 > < div id = " grid " > <% var grid = new WebGrid(source: Model, defaultSort: " ProductName " , rowsPerPage: 5 ); %> <%= grid.GetHtml( tableStyle: " grid " , headerStyle: " head " , alternatingRowStyle: " alt " , columns: grid.Columns( grid.Column(format: (item) => Html.ActionLink( " Edit " , " Edit " , new { id = item.ProductID })), grid.Column(format: (item) => Html.ActionLink( " Delete " , " Delete " , null , new { onclick = string .Format( " deleteRecord('Employee', '{0}') " , item.ProductID), @class = " Delete " , href = " JavaScript:void(0) " })), grid.Column( " ProductName " , " 产品名称 " ), grid.Column( " QuantityPerUnit " , " 每单位数量 " ), grid.Column( " UnitPrice " , " 单价 " ), grid.Column( " UnitsInStock " , " 库存单位 " ), grid.Column( " UnitsOnOrder " , " 订单单位 " ), grid.Column( " ReorderLevel " , " 重新排序级别 " ), grid.Column( " Discontinued " , " 已停产 " ) ) ) %> </ div > </ asp:Content > Razor: 代码 @model List < WebGridRazor.Models.Products > @{ View.Title = " 产品列表 " ; } < p > < h2 > 产品列表 </ h2 > < div id = " grid " > @{ var grid = new WebGrid(source: Model, defaultSort: " ProductName " , rowsPerPage: 3 ); } @grid.GetHtml( tableStyle: " grid " , headerStyle: " head " , alternatingRowStyle: " alt " , columns: grid.Columns( grid.Column(format: (item) => Html.ActionLink( " Edit " , " Edit " , new { id = item.ProductID })), grid.Column(format: (item) => Html.ActionLink( " Delete " , " Delete " , null , new { onclick = string .Format( " deleteRecord('Product', '{0}') " , item.ProductID), @class = " Delete " , href = " JavaScript:void(0) " })), grid.Column( " ProductName " , " 产品名称 " ), grid.Column( " QuantityPerUnit " , " 每单位数量 " ), grid.Column( " UnitPrice " , " 单价 " ), grid.Column( " UnitsInStock " , " 库存单位 " ), grid.Column( " UnitsOnOrder " , " 订单单位 " ), grid.Column( " ReorderLevel " , " 重新排序级别 " ), grid.Column( " Discontinued " , " 已停产 " ) ) ) </ div > </ p > Razor去掉了那些模板页的代码,使代码看起来更整洁。比较喜欢Razor。 总结:本文很简单,介绍了一下ASP.NET MVC 3 Beta中新功能WebGrid,由于这种方式WebGrid是在内存中分页和排序的,所以不适合大数据量。 代码:http://files.cnblogs.com/zhuqil/MvcApplication11.rar 本文转自麒麟博客园博客,原文链接:http://www.cnblogs.com/zhuqil/archive/2010/10/17/ASP-NET-MVC-3-Beta-WebGrid.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

自动化接口回归测试神器 AREX 使用初体验

AREX 是一款开源的基于流量录制回放技术的自动化回归测试平台,目前官方文档仅介绍了如何在本地及私有云部署,本篇文章分享如何 AWS 环境下快速搭建 AREX 服务,并使用 AWS 的 DocumentDB 作为数据库替换官方默认的 MongoDB,使用 ElastiCache 替换默认的 Redis。 安装 AREX 使用前需要注册 AWS 账号并对相应概念有一定了解,详细可参考 AWS 官方文档。 步骤一:准备一台 EC2 用于部署 AREX 操作系统选择 Amazon Linux 2 AMI。(Amazon Linux 2023 AMI 有部分应用安装不上,所以这里选择使用稳定的低版本操作系统) 如果只是进行试用,建议最小配置选择 t3.large(2C8G)的机器。 密钥对名称可以按照自己的需求创建并使用(方便快速链接到自己的 EC2)。后续创建 DocumentDB 的时候也可以共用该秘钥。 存储配置默认 8G 即可。 步骤二:创建 Amazon DocumentDB 集群 注意:在使用 DocumentDB 时,需要将其部署在与之关联的 EC2 实例所在的同一个虚拟私有云(VPC)中,保证它们之间能够进行通信。 创建 2 个 db.t3.medium 类型的实例,1 主 1 从两台机器,引擎版本选择 5.0.0。 安装完成后,配置对应的入站规则以允许外部网络通过连接串进行访问。使用 mongo shell 验证连接串是否可用。 执行 show dbs 命令,出现上图中红框部分则表示 DocumentDB 创建成功。 步骤四:准备一台 ElastiCache 并创建 Redis 集群 在 AWS 控制台中搜索 ElastiCache 并创建 Redis 集群。注意:ElastiCache 必须与上述创建的 EC2 在同一个 VPC 中。 选择配置并创建新集群,试用阶段可以先禁用集群模式,正式使用时按需修改配置。 引擎版本选择 6.2,节点类型选择 cache.t3.micro,副本数量设置为 0。 选择创建新的子网组,选择和上述 EC2 同一个 VPC ID。 在 EC2 上通过 redis-cli 连接到 ElastiCache 检查是否连通。如果连接不上可以看下安全组对应的入站规则,根据自己网络情况进行配置即可。 步骤五:通过 docker-compose 安装 AREX AREX 的安装非常简单,使用 Docker-Compose 命令,即可一键安装 AREX 所有基础服务组件。 这里简单介绍一下 AREX 的工作原理及各个服务组件。 AREX 回归测试的工作原理是利用 AREX Java Agent 将生产环境中 Java 应用的数据流量和请求信息进行采样录制,并将这些信息发送给 AREX 数据存取服务(Storage Service),由数据存取服务导入数据库(mongoDB)中进行存储。当需要进行回放时,AREX 调度服务(Schedule Service)将会通过 Storage Service 从数据库中提取被测应用的录制数据,然后向目标验证服务发送接口请求。同时,Java Agent 会将录制的外部依赖的响应进行 Mock,代替真正的数据访问,传达给被测应用,目标服务处理完成请求逻辑后返回响应报文。随后调度服务会将录制的响应报文与回放的响应报文进行比对,验证系统逻辑正确性,并将比对结果推送给分析服务(Report Service),由其生成完整的回放测试报告,供测试人员分析录制回放差异。 首先,通过 git 命令克隆 AREX 仓库。 git clone --depth 1 https://github.com/arextest/deployments.git cd deployments 配置 DocumentDB、ElastiCache 如要使用 AWS 的 DocumentDB 作为数据库替换官方默认的 MongoDB,并使用 ElastiCache 替换默认的 Redis,只需修改配置文件 docker-compose.yml 中的连接串,把文件中所有 MongoDB 的连接串都替换成 DocumentDB 的连接串,所有 Redis 的连接串都替换成 ElastiCache 的连接串即可。 步骤六:启动 AREX 配置完成后,执行 docker-compose 一键启动 AREX 服务。 docker-compose up -d 服务启动后,在没有修改端口配置的情况下,直接访问 8088 端口进入 AREX 前端页面。 差异分析 在实际使用过程中,对于一个复杂的线上应用,业务场景复杂,录制及回放的用例数量巨大,如何分析差异点及排查问题成为难点。 为了减轻使用者分析差异时的工作量,AREX 对可能存在的大量差异点,使用聚合的方法进行了大幅度的简化。 差异场景聚合分析 首先是差异场景聚合,AREX 对差异场景相同的多个用例进行了聚合展示。在介绍差异场景聚合逻辑之前,先大概了解一下 AREX 中用例(Case)的基本概念。 AREX 用例概念 在 AREX 中,一个用例通常由多个步骤组成,每个步骤包含了一个请求和一个响应。请求可以是主入口,也可以是外部调用(包括 DB、Redis 等)。在每个步骤中,都会记录请求的参数和响应结果等信息,用于后续的对比。如果录制与回放时的主入口响应,以及外部依赖的请求均无差异,则视为该用例回放通过。 这里的主入口和外部调用我们称之为 Mock 的类型。 Mock 的差异类型 每个 Mock 类型的对比差异类型会被分为三种情况: new call:这种差异类型表示该主入口或外部调用的 Mock 在录制时不存在,但在回放时存在,即新增了调用,通常是因为有新功能的迭代。 call missing:表示在录制时存在,但在回放时缺失了调用,通常是因为项目进行了优化,移除了某些不必要的调用关系。 value diff:表示在录制和回放时都存在,但在对比过程中某些节点有差异。后续章节中会具体介绍如何分析这些差异。 差异场景聚合 在进行流量回放测试时,针对可能出现的用例数量较大的情况,AREX 会通过一些聚合的操作,将相似的用例进行合并,以减少差异点的数量,便于用户对数据进行分析。如下图所示,聚合相似差异场景后,每个差异场景下仅选取一条用例作为展示。 场景聚合逻辑 有了上述 Mock 差异类型的概念,接下来介绍下差异用例场景聚合的逻辑。差异用例场景聚合是为了将具有相同 Mock 类型和差异类型的用例聚合在一起形成一个场景,从而帮助用户更快速地了解整个场景中的用例情况,减少用户需要分析的用例数量,提高分析用例的效率。 首先,根据 Mock 的 type 类型和差异类型的组合,AREX 会生成一个唯一的键,将所有用例分类聚合到这些键中,形成一个大分类。如上图中标注的 ①大分类。 其次,每个大分类中的用例都会再根据具体的 Mock 和差异类型的排列生成一个子唯一键,进一步对用例进行分类。这样做的目的是为了更加细致地分类,以便更快速地分析差异用例,具体可见上图中 ②小分类 的示例。每个小分类中有多少个用例数量会标记在该分类的最前面。 差异点分析 其次在每个差异场景中,AREX 对相似的差异节点也进行了聚合展示。 差异点聚合逻辑 在某些大报文的场景下,有些大数组中的差异点会非常多,一方面不利于前端展示,另一方面增加了使用者分析差异点的复杂度。 为了解决这个问题,AREX 将差异点按照模糊路径进行聚合。这里的模糊路径指的是不带数组下标的 JSON 节点路径。例如,一个 JSON 对象中有一个名为 “items” 的数组,数组中有多个元素 “items[0]”、“items[1]”、“items[2]” 等。在模糊路径中,这些路径会被合并为 “items”,从而实现聚合。 新增节点 在比较两个 JSON 对象时,如果发现某一边 JSON 对象中存在一个节点,但是另一边的对象中不存在该节点,那么我们就认为该节点是“新增节点”,如下图中,仅出现在右侧对象中的 cardpayfee 节点。同时为了方便用户查看,AREX 会将新增节点的所有祖先节点都标注为褐色,以突出显示该节点的位置。这样,用户可以清晰地看到该节点在另一侧对象中的位置,从而更好地分析问题。 差异节点 如果左右两边对象都存在某节点,但节点的值不一致,就可以认为该节点是“差异节点”,AREX 会使用蓝色背景高亮显示这些节点。不同于新增节点,这种类型的差异点只会出现在叶子节点上,因为只有叶子节点才有具体的值可以进行比较。 AREX 文档:http://arextest.com/zh-Hans/docs/intro/ AREX 官网:http://arextest.com/ AREX GitHub:https://github.com/arextest AREX 官方 QQ 交流群:656108079

优秀的个人博客,低调大师

PPOCRLabel 半自动化图形标注工具初体验

Windows + Anaconda 下载安装Anaconda(Python 3+) conda install pyqt=5 cd ./PPOCRLabel # 将目录切换到PPOCRLabel文件夹下 pyrcc5 -o libs/resources.py resources.qrc python PPOCRLabel.py 按官方文档执行完上述命令 报错1:is 和 is not不能识别,手动替换对应py文件中的 is 为‘==’ is not 替换为 ‘!=’ 报错2: 明明已经安装了,还是报这个错!因为是非专业的python小白,折腾了很久,最后直接 将anaconda3\Lib\site-packages下对应包复制到PPOCRLabel目录下即可!后续还报了个lxml模块找不到的错误,也是同样的解决方法! 效果图如下所示: 下载安装命令 ## CPU版本安装命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle ## GPU版本安装命令 pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu >>访问 PaddlePaddle 官网,了解更多相关内容。

优秀的个人博客,低调大师

一位 Rust 开发者的 Go 初体验

作者介绍:Nick Cameron,PingCAP 研发工程师,Rust 语言核心成员。 感谢 Rust 语言中文社区伙伴们的翻译和审校: 翻译:尚卓燃 审校:吴聪、张汉东 过去几周,我一直在用 Go 语言编写程序。这是我首次在大型且重要的项目中使用 Go。在研究 Rust 的特性时,我也看了很多关于 Go 的内容,包括体验示例和编写玩具程序。但真正用它编程又是一种完全不同的体验。 我觉得把这次体验写下来应该会很有趣。在这篇文章中,我会尽量避免将 Go 与 Rust 进行过多的比较,不过,由于我是从 Rust 转向 Go,难免也会包含一些比较。应该事先声明的是,我更偏袒 Rust ,但会尽力做到客观。 总体印象 用 Go 编程的感觉很棒。库程序里有我想要的一切,总体实现较为完善。学习体验也十分顺畅,不得不说,Go 是一种经过精心设计的实用性语言。举个例子:一旦你知悉了 Go 的语法,就能将其他语言中惯用法延续到 Go 中。只要你学会一些 Go,就可以相对轻易地推测 Go 语言的其他特性。凭借一些来自其他语言的知识,我能够阅读并理解 Go 代码,而不需要过多的搜索(Google)。 与 C/C++、Java、Python 等相比,Go 并没有那么多痛点,而且更具生产力。然而,它还是与这些语言处在同一个时代。尽管它从其他语言身上吸取了一些教训,甚至我个人认为它可能是那一代语言中最好的那个,但绝对还属于那一代语言。这是一种渐进式的改进,而不是推陈出新(需要明确的是,这不是意味着对其价值的批判,从软件工程的角度,渐进式改进通常会带来好的影响)。一个很好的例证是 nil:像 Rust 和 Swift 这样的语言已经去除了 null 的概念,并且消除了相关的一整类错误。Go 降低了一部分风险:没有空值(no null values),在 nil 和 0 之间进行区分。但其核心思想仍未改变,同样还会出现解空指针引用这种常见的运行时错误。 易学性 Go 非常易学。我知道人们经常吹捧这一点,但是我真的为自己生产力的飞速提高而感到震惊。多亏了 Go 语言以及它的文档和工具,我仅仅花了两天时间就可以写出「有价值」、可以提交的代码。 有助于易学性的几个因素是: Go 很精简。很多语言都试图让自己看起来小巧,但 Go 真正做到了这一点(这基本上是一件好事,我对这种自律精神印象深刻)。 标准库很出色(同样,也很小)。从生态系统中寻找并使用库程序非常容易。 几乎没有其他语言中所不具备的东西。Go 从其他既存语言中提取了很多内容,并进行完善,最后将它们很好地组合在一起。它在避免标新立异这一方面做了极大努力。 乏味的样板式代码 Go 代码很快就会变得非常重复。这是由于它缺乏宏或者泛型这种用于减少重复的机制(接口虽然有利于抽象,但在减少代码重复方面作用没有那么大)。最终我会写很多函数,而他们除了类型不同之外其他甚至完全一样。 错误处理也会导致重复。许多函数中像 if err != nil { return err } 这样的样板式代码甚至比那些真正有价值的代码还要多。 使用泛型或宏来减少样板式代码有时会受到批评,理由是不应为使代码易于编写而使其丧失可读性。我发现 Go 恰恰提供了一个反例,复制和粘贴代码往往既快速又简单,阅读代码却会令人灰心丧气,因为你不得不忽略大量的无关代码或者在大量的相同代码中找到细微的不同。 我喜欢的东西 编译时间:绝对快,可以确定要比 Rust 快得多。但实际上,它并没有我预期的那么快(对于中型到大型项目,我感觉它的速度只是与 C/C++ 相接近,或者稍微快一点。而我更加期待能够即时编译)。 协程(goroutine)和信道(channel):值得称赞的是,Go 为生成协程和使用信道提供了轻量级的语法。尽管只是一个小细节,却使 Go 的并发编程体验比其他语言更优越,它真正揭示了语法的力量。 接口:它们并不复杂,但是很容易理解和使用,并且在很多地方都很实用。 if ...; ... { } 语法:可以将变量的作用域限制在 if 语句真的很好。这与 Swift 及 Rust 中的 if let 起着相似的效果,但用途更为广泛(Go 没有像 Swift 和 Rust 那样的模式匹配,所以它无法使用 if let )。 测试和文档注释都很容易使用。 Go 工具链非常友好:将所有东西都放在一个地方,而不需要在命令行上使用多个工具。 拥有垃圾收集器(GC):不用考虑内存管理真的会使编程更加轻松。 可变参数。 我不喜欢的东西 以下内容没有特定的顺序。 nil 切片:要知道 nil、nil 切片和空切片三者都不相同,我敢保证我们只需要其中的两个,而不需要第三个。 枚举类型并不是第一公民:使用常量模拟枚举让人感觉是一种倒退。 不允许循环引用:这实际上限制了包在划分项目模块中的可用性,因为它变相鼓励了在一个包中堆积大量文件(或拥有大量零碎的小包,如果本该放在一起的文件四处分散,这也同样糟糕)。 switch 允许出现遗漏匹配的情况。 for ... range 语句会返回一对「索引/值」。要想只获取索引很容易(忽略值就好);但若要只获取值,则需要显式声明。在我看来,这种做法更应该颠倒过来,因为在大多数情况下,我更需要值而不是索引。 语法: 定义与用途存在不一致。 编译器有时会很挑剔(例如,要求或禁止尾随逗号);通过良好的工具可以缓解这种困扰,但是有时仍然会产生一些恼人的额外步骤。 使用多值返回类型时,类型上需要括号,但 return 语句中却不需要。 声明一个结构体需要两个关键字(type 和 struct)。 采用大写命名法来标记公共或私有变量,看起来就像匈牙利命名法那样,但更糟糕。 隐式接口。我知道它也出现在我喜欢的东西中,但有时候它确实很惹人烦——特别是当你试图找出所有实现该接口的类型,或者哪些接口是为给定类型而实现的时候。 你无法在不同的包中编写带有接收器的函数,所以即使接口是「鸭子类型」的,你也不能为其他包中的类型实现这个接口,这使得它们的用处大大降低。 还有我之前已经提过的,Go 缺少泛型和宏。 一致性 作为一名语言设计者和程序员,Go 最让我惊讶的地方也许是它的内置功能和用户可用功能之间频频出现不一致。许多语言的目标之一就是尽可能消除编译器魔法,让用户也能使用内置功能。运算符重载是一个简单但有争议的例子。但 Go 有很多魔法!你很容易就会遇到这样的问题:无法做那些内置功能可以做的事情。 一些让我印象深刻的地方: 返回多个值和信道的语法很棒,但是这两个无法一起使用,因为没有元组类型。 能够用 for ... range 语句对数组和切片进行迭代,但对其他集合就无能为力了,因为它缺乏迭代器的概念。 像 len 或者 append 这样的函数是全局函数,但你自己的函数却无法转变成全局函数。这些全局函数只能使用内置类型。即便 Go「没有泛型」,它们也可以变得通用。 没有运算符重载,那么 == 就会使人感到恼火。因为这意味着你不能在词典中使用自定义类型作为键,除非它们是可比较的。这一属性派生自类型结构,程序员无法重写该属性。 总结 Go 是一种简单、小巧、令人愉悦的语言。它也有一些犄角旮旯,但绝大部分是经过精心设计的。它的学习速度令人难以置信,并且规避了其他语言中一些不那么广为人知的特性。 Go 也是一种与 Rust 截然不同的语言。虽然两者都可以笼统地描述为「系统语言」或「C 语言的替代品」,但它们的设计目标、应用领域、语言风格和优先级不尽相同。垃圾收集确实带来了一个巨大的差异:使用 GC 使得 Go 变得更简单、更小,也更容易理解。而不使用 GC 使 Rust 奇快无比(特别是在您需要保证延迟,而不仅仅是高吞吐量的时候),并且得以支持 Go 中不可能实现的特性或编程模式(或者至少在不牺牲性能的前提下是无法实现的)。 Go 是一种编译型语言,其运行时得到了良好的实现,其速度毋庸置疑。Rust 也是编译型语言,但是运行时要小得多,它真的迅捷无比。在没有其他限制的情况下,我认为选择使用 Go 还是 Rust 其实意味着一种权衡: 一方面,Go 的学习曲线更短、程序更简单(这意味着更快的开发速度); 另一方面,Rust 真的性能卓越,并且类型系统更富有表现力(这使程序更安全,也意味着更快的调试和错误查找)。 附:英文原版文章 欢迎大家在下方留言,说说你的 Go & Rust 体验吧:从入门到放弃,从放弃到精通,踩过的坑,流过的泪……或者说说它们的必杀优势。 ⚠️理智探讨,Peace & Love.

优秀的个人博客,低调大师

Hadoop初体验:快速搭建Hadoop伪分布式环境

本文旨在使用一个全新安装好的Linux系统从0开始进行Hadoop伪分布式环境的搭建,以达到快速搭建的目的,从而体验Hadoop的魅力所在,为后面的继续学习提供基础环境。 对使用的系统环境作如下说明: 操作系统:CentOS 6.5 64位 主机IP地址:10.0.0.131/24 主机名:leaf 用户名:root hadoop版本:2.6.5 jdk版本:1.7 可以看到,这里直接使用root用户,而不是按照大多数的教程创建一个hadoop用户来进行操作,就是为了达到快速搭建Hadoop环境以进行体验的目的。 为了保证后面的操作能够正常完成,请先确认本机是否可以解析到主机名leaf,如果不能,请手动添加解析到/etc/hosts目录中: 1 2 3 4 5 6 [root@leaf~] #echo"127.0.0.1leaf">>/etc/hosts [root@leaf~] #pingleaf PINGleaf(127.0.0.1)56(84)bytesofdata. 64bytesfromlocalhost(127.0.0.1):icmp_seq=1ttl=64 time =0.043ms 64bytesfromlocalhost(127.0.0.1):icmp_seq=2ttl=64 time =0.048ms 64bytesfromlocalhost(127.0.0.1):icmp_seq=3ttl=64 time =0.046ms 1.rsync软件安装 使用下面命令安装: 1 [root@leaf~] #yuminstall-yrsync 2.ssh安装与免密码登陆配置 (1)ssh安装 使用下面命令安装 1 [root@leaf~] #yuminstall-yopenssh-serveropenssh-clients (2)ssh免密码登陆配置 因为Hadoop使用ssh协议来管理远程守护进程,所以需要配置免密码登陆。 关闭防火墙和selinux 为了确保能够成功配置,在配置前,先把防火墙和selinux关闭: 1 2 3 4 5 6 7 8 9 10 #关闭防火墙 [root@leaf~] #/etc/init.d/iptablesstop [root@leaf~] #chkconfig--level3iptablesoff #关闭selinux [root@leaf~] #setenforce0 [root@leaf~] #sed-is/SELINUX=enforcing/SELINUX=disabled/g/etc/selinux/config [root@leaf~] #cat/etc/selinux/config|grepdisabled #disabled-NoSELinuxpolicyisloaded. SELINUX=disabled 生成密钥对 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@leaf~] #mkdir.ssh [root@leaf~] #ssh-keygen-tdsa-P''-f.ssh/id_dsa Generatingpublic /private dsakeypair. Youridentificationhasbeensaved in . ssh /id_dsa . Yourpublickeyhasbeensaved in . ssh /id_dsa .pub. Thekeyfingerprintis: 5b:af:7c:45:f3:ff: dc :50:f5:81:4b:1e:5c:c1:86:90root@leaf Thekey'srandomartimageis: +--[DSA1024]----+ |.ooo.| |E..oo| |=...| |o=+| |S.+oo| |o....| |.....| |...oo| |o.=| +-----------------+ 将公钥添加到本地信任列表 1 [root@leaf~] #cat.ssh/id_dsa.pub>>.ssh/authorized_keys 验证 上面三步完成后就完成了免密码登陆的配置,可以使用下面的命令进行验证: 1 2 3 4 5 6 7 [root@leaf~] #sshlocalhost Theauthenticityofhost 'localhost(::1)' can'tbeestablished. RSAkeyfingerprintisd1:0d:ed:eb:e7:d1:2f:02:23:70:ef:11:14:4e:fa:42. Areyousureyouwantto continue connecting( yes /no )? yes Warning:Permanentlyadded 'localhost' (RSA)tothelistofknownhosts. Lastlogin:WedAug3004:28:012017from10.0.0.1 [root@leaf~] # 在第一次登陆的时候需要输入yes,之后再登陆时就可以直接登陆了: 1 2 3 [root@leaf~] #sshlocalhost Lastlogin:WedAug3004:44:022017fromlocalhost [root@leaf~] # 3.jdk安装与配置 (1)jdk下载 这里使用的是jdk1.7版本,可以到下面的网站进行下载: http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7-521261.html 下载完成后,可以使用winscp上传到/root目录下,如下: 1 2 [root@leaf~] #ls-lhjdk-7u80-linux-x64.tar.gz -rw-r--r--.1rootroot147MAug2912:05jdk-7u80-linux-x64. tar .gz (2)jdk安装 将jdk解压到/usr/local目录下,并创建软链接: 1 2 3 4 5 6 7 8 [root@leaf~] #cpjdk-7u80-linux-x64.tar.gz/usr/local/ [root@leaf~] #cd/usr/local/ [root@leaf local ] #tar-zxfjdk-7u80-linux-x64.tar.gz [root@leaf local ] #ls-ldjdk1.7.0_80/ drwxr-xr-x.8uucp1434096Apr112015jdk1.7.0_80/ [root@leaf local ] #ln-sjdk1.7.0_80/jdk [root@leaf local ] #ls-ldjdk lrwxrwxrwx.1rootroot12Aug3004:56jdk->jdk1.7.0_80/ (3)JAVA_HOME环境变量配置 java命令在/usr/local/jdk/bin目录下: 1 2 3 [root@leaf local ] #cdjdk/bin/ [root@leafbin] #ls-lhjava -rwxr-xr-x.1uucp1437.6KApr112015java 配置java环境变量: 1 2 3 [root@leafbin] #echo'exportJAVA_HOME=/usr/local/jdk/bin'>>/etc/profile [root@leafbin] #echo'exportPATH=$PATH:$JAVA_HOME'>>/etc/profile [root@leafbin] #source/etc/profile 这样我们就可以在任何一个目录下使用java相关的命令: 1 2 3 4 5 6 [root@leaf~] #java-version javaversion "1.7.0_80" Java(TM)SERuntimeEnvironment(build1.7.0_80-b15) JavaHotSpot(TM)64-BitServerVM(build24.80-b11,mixedmode) [root@leaf~] #javac-version javac1.7.0_80 4.hadoop安装与配置 (1)hadoop下载 这里使用hadoop 2.6.5版本,可以到下面的网站进行下载: http://hadoop.apache.org/releases.html 选择2.6.5的binary进入相应的页面便可以下载,然后使用winscp上传到/root目录下,如下: 1 2 [root@leaf~] #ls-lhhadoop-2.6.5.tar.gz -rw-r--r--.1rootroot191MAug2919:09hadoop-2.6.5. tar .gz (2)hadoop安装 将hadoop解压到/usr/local目录下,并创建软链接: 1 2 3 4 5 6 7 8 [root@leaf~] #cphadoop-2.6.5.tar.gz/usr/local [root@leaf~] #cd/usr/local [root@leaf local ] #tar-zxfhadoop-2.6.5.tar.gz [root@leaf local ] #ls-ldhadoop-2.6.5 drwxrwxr-x.9100010004096Oct32016hadoop-2.6.5 [root@leaf local ] #ln-shadoop-2.6.5hadoop [root@leaf local ] #ls-ldhadoop lrwxrwxrwx.1rootroot12Aug3005:05hadoop->hadoop-2.6.5 (3)hadoop环境变量配置 hadoop相关命令在/usr/local/hadoop/bin和/usr/local/hadoop/sbin目录下,如下所示: 1 2 3 [root@leaf local ] #cdhadoop/bin/ [root@leafbin] #ls-lhhadoop -rwxr-xr-x.1100010005.4KOct32016hadoop 配置hadoop环境变量: 1 2 [root@leafbin] #echo'exportHADOOP_HOME=/usr/local/hadoop/bin:/usr/local/hadoop/sbin'>>/etc/profile [root@leafbin] #echo'exportPATH=$PATH:$HADOOP_HOME'>>/etc/profile 这样我们就可以在任何一个目录下使用hadoop相关的命令: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@leaf~] #hadoop Usage:hadoop[--configconfdir]COMMAND whereCOMMANDisoneof: fsrunagenericfilesystemuserclient versionprinttheversion jar<jar>runajar file checknative[-a|-h]checknativehadoopandcompressionlibrariesavailability distcp<srcurl><desturl>copy file ordirectoriesrecursively archive-archiveNameNAME-p<parentpath><src>*<dest>createahadooparchive classpathprintstheclasspathneededtogetthe credentialinteractwithcredentialproviders Hadoopjarandtherequiredlibraries daemonlogget /set theloglevel for eachdaemon traceviewandmodifyHadooptracingsettings or CLASSNAMEruntheclassnamedCLASSNAME Mostcommandsprinthelpwheninvokedw /o parameters. (4)hadoop配置 hadoop的配置文件在/usr/local/hadoop/etc/hadoop目录下: 1 2 3 4 5 6 7 8 9 10 [root@leaf~] #cd/usr/local/hadoop/etc/hadoop/ [root@leafhadoop] #ls capacity-scheduler.xmlhadoop-policy.xmlkms-log4j.propertiesssl-client.xml.example configuration.xslhdfs-site.xmlkms-site.xmlssl-server.xml.example container-executor.cfghttpfs- env .shlog4j.propertiesyarn- env .cmd core-site.xmlhttpfs-log4j.propertiesmapred- env .cmdyarn- env .sh hadoop- env .cmdhttpfs-signature.secretmapred- env .shyarn-site.xml hadoop- env .shhttpfs-site.xmlmapred-queues.xml.template hadoop-metrics2.propertieskms-acls.xmlmapred-site.xml.template hadoop-metrics.propertieskms- env .shslaves 配置core-site.xml 1 2 3 4 5 6 <configuration> <property> <name>fs.default.name< /name > <value>hdfs: //localhost :9000< /value > < /property > < /configuration > fs.default.name这个字段下的值用于指定NameNode(HDFS的Master)的IP地址和端口号,如下面的value值hdfs://localhost:9000,就表示HDFS NameNode的IP地址或主机为localhost,端口号为9000. 配置hdfs-site.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <configuration> <property> <name>dfs.replication< /name > <value>1< /value > < /property > <property> <name>dfs.name. dir < /name > <value> /root/hdfs-filesystem/name < /value > < /property > <property> <name>dfs.data. dir < /name > <value> /root/hdfs-filesystem/data < /value > < /property > < /configuration > dfs.replication用于指定HDFS中每个Block块被复制的次数,起到数据冗余备份的作用;dfs.name.dir用于配置HDFS的NameNode的元数据,以逗号隔开,HDFS会把元数据冗余复制到这些目录下;dfs.data.dir用于配置HDFS的DataNode的数据目录,以逗号隔开,HDFS会把数据存在这些目录下。 配置mapred-site.xml 1 2 3 4 5 6 <configuration> <property> <name>mapred.job.tracker< /name > <value>localhost:9001< /value > < /property > < /configuration > mapred.job.tracker字段用于指定MapReduce Jobtracker的IP地址及端口号,如这里IP地址或主机为localhost,9001是MapReduce Jobtracker RPC的交互端口。 配置hadoop-env.sh 1 export JAVA_HOME= /usr/local/jdk 5.hadoop启动与测试 (1)格式化HDFS分布式文件系统 执行如下命令: 1 2 3 4 5 6 7 8 [root@leaf~] #hadoopnamenode-format ... 17 /08/30 08:41:29INFOnamenode.NNStorageRetentionManager:Goingtoretain1imageswithtxid>=0 17 /08/30 08:41:29INFOutil.ExitUtil:Exitingwithstatus0 17 /08/30 08:41:29INFOnamenode.NameNode:SHUTDOWN_MSG: /************************************************************ SHUTDOWN_MSG:ShuttingdownNameNodeatleaf /127 .0.0.1 ************************************************************/ 注意看输出显示是不是跟上面的类似,如果是,则说明操作成功。 (2)启动hadoop服务 执行如下命令: 1 2 3 4 5 6 7 8 9 10 11 12 [root@leaf~] #start-all.sh ThisscriptisDeprecated.Insteadusestart-dfs.shandstart-yarn.sh 17 /08/30 08:53:22WARNutil.NativeCodeLoader:Unabletoloadnative-hadooplibrary for yourplatform...using builtin -javaclasseswhereapplicable Startingnamenodeson[localhost] localhost:startingnamenode,loggingto /usr/local/hadoop-2 .6.5 /logs/hadoop-root-namenode-leaf .out localhost:startingdatanode,loggingto /usr/local/hadoop-2 .6.5 /logs/hadoop-root-datanode-leaf .out Startingsecondarynamenodes[0.0.0.0] 0.0.0.0:startingsecondarynamenode,loggingto /usr/local/hadoop-2 .6.5 /logs/hadoop-root-secondarynamenode-leaf .out 17 /08/30 08:53:48WARNutil.NativeCodeLoader:Unabletoloadnative-hadooplibrary for yourplatform...using builtin -javaclasseswhereapplicable startingyarndaemons startingresourcemanager,loggingto /usr/local/hadoop-2 .6.5 /logs/yarn-root-resourcemanager-leaf .out localhost:startingnodemanager,loggingto /usr/local/hadoop-2 .6.5 /logs/yarn-root-nodemanager-leaf .out (3)hadoop服务测试 启动完成后,执行jps命令,可以看到hadoop运行的守护进程,如下: 1 2 3 4 5 6 7 [root@leaf~] #jps 4167SecondaryNameNode 4708Jps 3907NameNode 4394NodeManager 4306ResourceManager 3993DataNode 也可以通过在浏览器中输入地址来访问相关页面,这里访问NameNode的页面,地址为http://10.0.0.131:50070,如下: 访问DataNode的页面,地址为http://10.0.0.131:50075,如下 本文转自 xpleaf 51CTO博客,原文链接:http://blog.51cto.com/xpleaf/1960982,如需转载请自行联系原作者

优秀的个人博客,低调大师

阿里云Kubernetes服务上使用Tekton完成应用发布初体验

Tekton 是一个功能强大且灵活的 Kubernetes 原生开源框架,用于创建持续集成和交付(CI/CD)系统。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。 本文是基于阿里云Kubernetes服务部署Tekton Pipeline,并使用它完成源码拉取、应用打包、镜像推送和应用部署的实践过程。 Tekton Pipeline中有5类对象,核心理念是通过定义yaml定义构建过程.构建任务的状态存放在status字段中。 其中5类对象分别是:PipelineResouce、Task、TaskRun、Pipeline、PipelineRun。 Task是单个任务的构建过程,需要通过定义TaskRun任务去运行Task。 Pipeline包含多个Task,并在此基础上定义input和output,input和out

优秀的个人博客,低调大师

阿里云Kubernetes Service Mesh实践进行时(1): Istio初体验

概述 [Announcement] Istio第一个生产可用版本1.0于2018年7月31日正式发布。 注意:在使用阿里云Kubernetes容器服务Istio 1.0的过程中,如果遇到类似CRD版本问题,请参考我们提供的问题分析。 我们会持续更新遇到的问题及其解决方法。 Istio是一个用于连接/管理以及安全化微服务的开放平台,提供了一种简单的方式用于创建微服务网络,并提供负载均衡、服务间认证以及监控等能力,并且关键的一点是并不需要修改服务本身就可以实现上述功能。 Istio主要提供以下功能: 流量管理: 控制服务之间调用的流量和API调用; 使得调用更可靠,并使网络在恶劣情况下更加健壮; 可观察性: 获取服务之间的依赖,以及服务调用的流量走向; 从而提供快速识别问题的能力; 策略执行:控制服务的访问策略,不需要改动服务本身; 服务身份和安全:

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册