• 关于

    控制器调用模板

    的搜索结果

回答

本文介绍OOS的几个基本概念,以便于您更好地理解运维编排服务OOS产品。 名称 定义 模板 一个YAML或JSON格式的文件,包括一个或多个任务。 任务 模板的主要组成部分,定义了具体的动作细节。每一个任务都是一个动作的具象化使用。 动作 定义了一个具体的操作,为操作的最小单位。 动作分为几种类型:API动作(Atom Action)、云产品动作(Product Action)、触发器动作(Trigger Action)、控制类动作(Control Action)和嵌套类动作(ACS::Template)。 名称格式为ACS:: 。 API动作 允许调用API的动作,目前支持ACS::ExecuteAPI、ACS::WaitFor和ACS::CheckFor。 云产品动作 根据云产品的业务和OpenAPI特点封装的动作,用于简化模板的编写。例如,ACS::ECS::RunInstances Action执行成功后,将确保所创建的实例进入了可使用的状态(如运行中Running)。 名称格式为ACS:: :: 。 触发器动作 开启后,当触发源满足条件时,即开始执行一个模板。当前支持的触发器动作有定时器(TimerTrigger)。 控制类动作 控制执行的顺序或执行方式等,如审批动作ACS::Approve。 嵌套类动作 目前只支持ACS::Template,该动作允许引用其他模板(子模板Child Template)作为任务,支持自定义可以复用的子模板。包含引用其他子模板的模板叫做父模板(Parent Template)。 执行 模板执行中或执行后的结果。 任务执行 任务在执行中或执行后的结果。 循环任务 表示该任务(Task)会循环使用一个列表内的值并执行。

1934890530796658 2020-03-24 11:22:18 0 浏览量 回答数 0

回答

1.Servlet 相当于一个控制器,页面把请求数据提交给它,然后 它调用方法处理并返回给用户 结果页面 2.mvc意思是 模型层,视图层,控制层的意思。Servlet 是控制器也就是c(控制层),Javabean 是模型层里面封装着程序要处理的数据,jsp是视图层 程序把数据通过视图页面反馈给用户 建议你参考一下云栖社区这位朋友的文章:JavaWeb学习之Servlet(二)----Servlet的生命周期、继承结构、修改Servlet模板

zuntop_com 2019-12-02 02:10:41 0 浏览量 回答数 0

问题

MVC模式中控制器准备数据的一个疑问:报错

kun坤 2020-06-07 17:00:52 0 浏览量 回答数 1

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

联系点播后台开通服务,如何做。没有链接,没有联系方式

好成长 2019-12-01 18:53:23 429 浏览量 回答数 0

问题

深入理解Magento - 第二章 - Magento请求分发与控制器 400 请求报错 

kun坤 2020-05-28 16:31:47 5 浏览量 回答数 1

问题

链接问题(VC6)

游客ufivfoddcd53c 2020-01-03 16:35:33 1 浏览量 回答数 1

回答

Guns基于SpringBoot,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl!Guns项目代码简洁,注释丰富,上手容易,同时Guns包含许多基础模块(用户管理,角色管理,部门管理,字典管理等10个模块),可以直接作为一个后台管理系统的脚手架。Guns v3.0新增rest api服务,提供对接服务端接口的支持,并利用jwt token鉴权机制给予客户端的访问权限,传输数据进行md5签名保证传输过程数据的安全性!项目特点1、基于SpringBoot,简化了大量项目配置和maven依赖,让您更专注于业务开发,独特的分包方式,代码多而不乱。2、完善的日志记录体系,可记录登录日志,业务操作日志(可记录操作前和操作后的数据),异常日志到数据库,通过@BussinessLog注解和LogObjectHolder.me().set()方法,业务操作日志可具体记录哪个用户,执行了哪些业务,修改了哪些数据,并且日志记录为异步执行,详情请见@BussinessLog注解和LogObjectHolder,LogManager,LogAop类。3、利用beetl模板引擎对前台页面进行封装和拆分,使臃肿的html代码变得简洁,更加易维护。4、对常用js插件进行二次封装,使js代码变得简洁,更加易维护,具体请见webapp/static/js/common文件夹内js代码。5、利用ehcache框架对经常调用的查询进行缓存,提升运行速度,具体请见ConstantFactory类中@Cacheable标记的方法。6、controller层采用map + warpper方式的返回结果,返回给前端更为灵活的数据,具体参见com.stylefeng.guns.modular.system.warpper包中具体类。7、防止XSS攻击,通过XssFilter类对所有的输入的非法字符串进行过滤以及替换。8、简单可用的代码生成体系,通过SimpleTemplateEngine可生成带有主页跳转和增删改查的通用控制器、html页面以及相关的js,还可以生成Service和Dao,并且这些生成项都为可选的,通过ContextConfig下的一些列xxxSwitch开关,可灵活控制生成模板代码,让您把时间放在真正的业务上。9、控制器层统一的异常拦截机制,利用@ControllerAdvice统一对异常拦截,具体见com.stylefeng.guns.core.aop.GlobalExceptionHandler类。10、页面统一的js key-value单例模式写法,每个页面生成一个唯一的全局变量,提高js的利用效率,并且有效防止多个人员开发引起的函数名/类名冲突,并且可以更好地去维护代码。11、可以查看博文:热门开源项目:Guns-后台管理系统-博客-云栖社区-阿里云 https://yq.aliyun.com/articles/224607?spm=5176.8091938.0.0.aLr7RG

zwt9000 2019-12-02 00:24:40 0 浏览量 回答数 0

回答

调用API可能出现的错误有四类:连接淘宝服务器错误、平台级错误、业务级错误和容器类错误。这四种类型的错误分别代表了开发者访问淘宝服务器、淘宝接入平台、后端业务和容器这几个层次上出现的问题。 2,连接淘宝服务器错误主要是http连接错误或者连接被重置被拒绝等,这类错误是开发者访问淘宝服务器出现的问题,请直接联系服务器管理员或通过网络搜索答案。 平台级错误 1,平台级错误是指错误码小于100的调用错误,这种错误一般是由于用户的请求不符合各种基本校验而引起的。 2,用户遇到这些错误的返回首先检查应用的权限、频率等情况,然后参照文档检验一下传入的参数是否完整且合法。 错误码 错误描述-英文 错误描述-中文 解决方案 3 Upload Fail 图片上传失败 将传入的图片格式改为正确的格式、适当的大小的图片放进消息体里面传输过来,如果传输仍然失败需要减小图片大小或者增加网络带宽进行尝试 7 App Call Limited 应用调用次数超限,包含调用频率超限 调整程序合理调用API,等限频时间过了再调用,淘客的调用频率是系统按照上个月交易额自动修改的,修改后的频率会在官方论坛首页以公告形式通知,开发者可自行查看 9 Http Action Not Allowed HTTP方法被禁止 请用大写的POST或GET,如果有图片等信息传入则一定要用POST才可以 10 Service Currently Unavailable 服务不可用 多数是由未知异常引起的,仔细检查传入的参数是否符合文档描述 11 Insufficient ISV Permissions 开发者权限不足 应用没有权限调用中级或高级权限的接口,可在淘宝合作伙伴后台提交权限申请 12 Insufficient User Permissions 用户权限不足 应用没有权限调用中级或高级权限的接口,可在淘宝合作伙伴后台提交权限申请 13 Insufficient Partner Permissions 合作伙伴权限不足 应用没有权限调用中级或高级权限的接口,可在淘宝合作伙伴后台提交权限申请 15 Remote Service Error 远程服务出错 API调用后端服务出错,首先查看自己的参数是否合法,如果参数没有问题请过一段时间再尝试 21 Missing Method 缺少方法名参数 传入的参数加入method字段 22 Invalid Method 不存在的方法名 传入的method字段必需是你所调用的API的名称,并且该API是确实存在的 23 Invalid Format 无效数据格式 传入的format必需为json或xml中的一种 24 Missing Signature 缺少签名参数 传入的参数中必需包含sign字段 25 Invalid Signature 无效签名 签名必需根据正确的算法算出来的。算法请见: http://open.taobao.com/dev/index.php/API签名算法 26 Missing Session 缺少SessionKey参数 传入的参数中必需包含session字段 27 Invalid Session 无效的SessionKey参数 传入的session必需是用户绑定session拿到的,如果报session不合法可能是用户没有绑定session或session过期造成的,用户需要重新绑定一下然后传入新的sessionKey 28 Missing App Key 缺少AppKey参数 传入的参数必需包含app_key字段 29 Invalid App Key 无效的AppKey参数 应用所处的环境跟选择的环境不一致,例如:应用处于沙箱测试环境,却选择在正式环境进行测试,可在合作伙伴后台或商家接入平台对该应用进行修改 30 Missing Timestamp 缺少时间戳参数 传入的参数中必需包含timestamp参数 31 Invalid Timestamp 非法的时间戳参数 时间戳,格式为yyyy-mm-dd hh:mm:ss,例如:2008-01-25 20:23:30。淘宝API服务端允许客户端请求时间误差为10分钟 32 Missing Version 缺少版本参数 传入的参数中必需包含v字段 33 Invalid Version 非法的版本参数 用户传入的版本号格式错误,必需为数字格式 34 Unsupported Version 不支持的版本号 用户传入的版本号没有被提供 40 Missing Required Arguments 缺少必选参数 API文档中设置为必选的参数是必传的,请仔细核对文档 41 Invalid Arguments 非法的参数 参数类型不对,例如:需要传入的是数字类型的,却传入了字符类型的参数 42 Forbidden Request 请求被禁止 目前没有控制 43 Parameter Error 参数错误 一般是用户传入参数非法引起的,请仔细检查入参格式、范围是否一一对应 47 Invalid encoding 编码错误 一般是用户做http请求的时候没有用UTF-8编码请求造成的 业务级错误 1,业务级错误是指用户通过平台初步的参数校验,进入后端业务流程所出现的,错误码大于100的错误。 2,以isv开头的一般都是isv的错误,这一类错误一般是由于用户提供的参数不合法或者不匹配造成的,因此isv应该根据错误信息检验是否传入了相应的信息,对于这一类错误建议改正后再重试。 3,以isp开头的错误一般是isp服务不可用或top平台连接后端服务时的错误,这些错误可能与后台服务端的服务可用性有关,建议用户在一段时间后重试。 4,错误响应是用户和服务器交互失败的最直接展示,isv在调用top服务时,如果调用失败,请尽量保留下错误日志以便进行后面的错误追查。 业务级父错误 产品线 错误码 用户 500 类目 510 交易 520 退款 521 商品 530 商品扩展 531 邮费模板 532 产品 540 物流 550 店铺 560 评价 570 淘宝客 580 系统 590 备案 591 增量 600 画报 620 江湖 630 分销 640 淘秀 650 收费 660 业务级子错误 子错误码格式 错误信息 归属方 是否可在程序中重试 isv.###-not-exist:*** 根据***查询不到### ISV 否 isv.missing-parameter:*** 缺少必要的参数*** ISV 否 isv.invalid-paramete:*** 参数***无效,格式不对、非法值、越界等 ISV 否 isv.invalid-permission 权限不够、非法访问 ISV 否 isv.parameters-mismatch:***-and-### 传入的参数***和###不匹配,两者有一定的对应关系 ISV 否 isv.***-service-error:### 调用***服务返回false,业务逻辑错误,###表示具体的错误信息 ISV 否 isp.***-service-unavailable 调用后端服务***抛异常,服务不可用 ISP 是 isp.remote-service-error 连接远程服务错误 ISP 是 isp.remote-service-timeout 连接远程服务超时 ISP 是 isp.remote-connection-error 远程连接错误 ISP 是 isp.null-pointer-exception 空指针异常错误 ISP 否 isp.top-parse-error api解析错误(出现了未被明确控制的异常信息) ISP 否 isp.top-remote-connection-timeout top平台连接后端服务超时 ISP 是 isp.top-remote-connection-error top平台连接后端服务错误,找不到服务 ISP 是 isp.top-mapping-parse-error top-mapping转换出错,主要是由于传入参数格式不对 ISP 否 isp.unknown-error top平台连接后端服务抛未知异常信息 ISP 是 容器类错误 容器类错误是指用户通过容器登录之后页面上出现的错误 错误码 错误描述(中文) 100 授权码已经过期 101 授权码在缓存里不存在,一般是用同样的authcode两次获取sessionkey 102 系统错误,建议清理浏览器缓存,稍后重试 103 appkey或者tid(插件ID)参数必须至少传入一个 104 appkey或者tid对应的插件不存在 105 插件的状态不对,不是上线状态或者正式环境下测试状态 106 没权限调用此app,由于插件不是所有用户都默认安装,所以需要用户和插件进行一个订购关系,这个错误一般是由于用户访问了自己没有订购的在线订购应用所造成的 107 系统错误,建议清理浏览器缓存,稍后重试 108 应用是自用型应用,只有自用型绑定用户才可以访问。 109,111 服务端在生成参数的时候出了问题,建议清理浏览器缓存,稍后重试 110 服务端在写出参数的时候出了问题 ,建议清理浏览器缓存,稍后重试 112 回调地址不正确,请检查回调地址,是否为空,或者含有top认为非法的字符。 113 用户没有同意授权

问问小秘 2019-12-02 02:12:57 0 浏览量 回答数 0

问题

支付宝小程序云训练营优秀学员提问来啦

问问小秘 2020-06-15 15:57:38 159 浏览量 回答数 1

回答

用上 FreeMarker 后,再也不需要 JSP了,JSP相对来说弱爆了,连 layout 功能都没有,layout 现在是基本配置了###### 引用来自“爪哇老妖”的答案 这个真可以有,JSP、Velocity、Freemarker这些都属于动态模版技术,HTML呢,直接就是静态模版技术,也许你会反问我可以用ajax来实现后台调用数据啊,可是我很担心你那页面的加载速度和用户体验,情况更严重的是,有些页面的效果是根据后台数据或参数来展示的,对于你的这个想法,持保留意见。。。 从加载速度方面来说,并没有想像中那么慢,在Chrome和Firefox上速度飞快,加载HTML的时候根本感觉不到它做了些什么,只是在IE上不太理想,页面巨大的话有时候确实会卡一下,不过随着IE版本的更新这个情况可能会慢慢解决掉。 用户体验方面感觉比原来刷页面的方式好的多,Ajax与后台交互,所以根本看不到讨厌的500错误页面了,用JS处理好后台返回的错误,提示信息更加易懂。 至于页面显示内容与数据库有关的问题,这都是交给后台组织好数据用JSON返回的,例如:{userid:1,operation: {'查询'}}和{userid:2,operation: {'查询','删除'}}之类,这样JS拿到JSON数据之后就知道该怎么构建页面了,当然,这些页面元素级的控制,完全可以放到数据库中去,动态的实时控制用户权限。 ######楼主说的情况跟extjs一样,抛弃动态页面,所有数据用js控制,展现,IE处理js虽然慢一点,但这是可控的,在加载的时候给个loading图片用户看起来比浏览器加载动态页面效果好些。要是js功底好,还可以写一些特效,用户体验就更好了。######+freemarker###### 看情况啊,可以有。 不过你说的情况,也有很多,尤其是网站很大,甚至页面的不同部分都是不同的小组做的话。 这时候不用落伍的iframe,就用ajax,jsonp了。 ###### 引用来自“闫晓沛”的答案 +freemarker 是直接访问freemarker的模板吗?这样也确实可以代替JSP,不过还是得走一下Java容器,感觉不如直接让Apache处理HTML的好 ###### 这个真可以有,JSP、Velocity、Freemarker这些都属于动态模版技术,HTML呢,直接就是静态模版技术,也许你会反问我可以用ajax来实现后台调用数据啊,可是我很担心你那页面的加载速度和用户体验,情况更严重的是,有些页面的效果是根据后台数据或参数来展示的,对于你的这个想法,持保留意见。。。 ######回复 @lovinglily : ”世界上不会有一个页面是预先设计好的,因为它有数据“乔布斯说的######意思是,静态页面预先设计好布局的话,并不是根本意义上根据后台数据动态改变,这对页面数据显示有影响是吧..######你能保证所有用户的浏览器都能处理你的这些数据?######跨浏览器确实是一个大问题,可以借助jQuery这样的JS库来解决,虽然有些时候也不是很完美,但是够用了######对于jsp freemarker 等之类的都是属于动态模板,要知道它们最终都是翻译成html代码给浏览器,所以没有替代这种说法。只能说哪种情况更适合你的应用了。如果动态交互更多 且动态判断多的情况下 你用ajax动态请求的方式显得很蹩脚,这中间有个取舍和平衡点######同意你的观点###### 引用来自“爪哇老妖”的答案 这个真可以有,JSP、Velocity、Freemarker这些都属于动态模版技术,HTML呢,直接就是静态模版技术,也许你会反问我可以用ajax来实现后台调用数据啊,可是我很担心你那页面的加载速度和用户体验,情况更严重的是,有些页面的效果是根据后台数据或参数来展示的,对于你的这个想法,持保留意见。。。 我能保证所有用户浏览器都能处理服务器发给它的HTML,很难保证用户浏览器能处理我页面里的所有Ajax数据请求。 ######回复 @xmut : js不可能运行的很慢,就算被我们喷的无地自容的IE6,其实也是大量的js在运行。一个页面的js不可能很多,多到不能处理的地步,否则是本身就设计有问题的######啥年代了,还在纠结浏览器能不能运行js。######没错,这样的项目架构确实存在禁用JS之后玩不转的情况,但是现在有几个项目不用JS的呢,我们可以把“开启JS”作为项目运行必需的环境提出来######确实!楼主把页面控制逻辑全部交给javascript,万一客户端javascript被禁用,或者javascript运行速度很慢,这反而影响了客户体验!######有还是没有 ,看项目的需要,没有不能替代的技术方案。

爱吃鱼的程序员 2020-06-04 16:49:07 0 浏览量 回答数 0

回答

创建一个包含模板间共享布局的模板,通常这样的模板包含: 页面头部、导航栏、脚部、内容展示区域。 Layout.html <!DOCTYPE html> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <head> <title>Layout page</title> <script src="common-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section layout:fragment="content"> <p>Page content goes here</p> </section> <footer> <p>My footer</p> <p layout:fragment="custom-footer">Custom footer here</p> </footer> </body> </html> 注意事项: 1. html标签上附上命名空间 2. section与脚部p标签上使用layout:fragment属性 这些是布局中的插入候选槽点,通过匹配内容模板中片段进行替换。 创建一些内容模板: Content1.html <!DOCTYPE html> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{Layout}"> <head> <title>Content page 1</title> <script src="content-script.js"></script> </head> <body> <section layout:fragment="content"> <p>This is a paragraph from content page 1</p> </section> <footer> <p layout:fragment="custom-footer">This is some footer content from content page 1</p> </footer> </body> </html> html标签中的layout:decorate说明哪一个布局模板使用这个内容模板进行装饰。内容模板定义自身标题与脚本、content与custom-footer片段。custom-footer片段处于footer元素内部,这其实是不必要的,但是可能会是很方便的,如果想要做内容模板的静态模板,这是一开始使用Thymeleaf的原因之一。 在一个模板内片段名称必须唯一,否则可能会出现片段不匹配,各种各样的可笑事情会接踵而至。 不管如何,一旦告知Thymeleaf处理Content1.html,最终的页面会是这样子: <!DOCTYPE html> <html> <head> <title>Content page 1</title> <script src="common-script.js"></script> <script src="content-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section> <p>This is a paragraph from content page 1</p> </section> <footer> <p>My footer</p> <p>This is some footer content from content page 1</p> </footer> </body> </html> 内容模板装饰Layout.html,结果是布局的组合,加上内容模板的片段(两个模板的<head>元素,来自内容模板的<title>元素替换布局文件内的,所有的元素来自布局文件,但是由所有指定的内容模板进行替换) 想了解更多可以如何控制<head>元素合并,参看<head>元素合并一小节。 装饰进程重定向处理从内容模板至布局,将layout:fragment部分从内容模板中挑选出来,因为布局需要它们。正因如此,任何在layout:fragment之外的东西实际从未得到执行,这说明在内容模板中不能这样做: <div th:if="${user.admin}"> <div layout:fragment="content"> ... </div> </div> 如果布局模板想要’内容’片段,那么会得到那个片段,不顾任何所在条件,因为那些条件不会执行。 如果说只想用绝对最小HTML代码量替换装饰器脚部: Content2.html <p layout:decorate="~{Layout}" layout:fragment="custom-footer"> This is some footer text from content page 2. </p> 这就是全部所需的东西!<p>标签同时用作根元素与片段定义,生成一个像这样的页面: <!DOCTYPE html> <html> <head> <title>Layout page</title> <script src="common-script.js"></script> </head> <body> <header> <h1>My website</h1> </header> <section> <p>Page content goes here</p> </section> <footer> <p>My footer</p> <p> This is some footer text from content page 2. </p> </footer> </body> </html> 可以把布局看作母版,会得以填充或者被内容(子模板)覆盖,仅当内容会填充/覆盖父类。以这种方式,布局充当某种’默认’,内容充当这种默认之上的实现。 给布局传送数据 子模板向上给母版布局传送数据,在涉及到布局/装饰过程的任意元素上使用th:with/ data-th-with属性处理器,可以在任何地方layout:decorate/ data-layout-decorate 或者可以发现 layout:fragment/data-layout-fragment, 例如: 孩子/内容模板: <html layout:decorate="your-layout.html" th:with="greeting='Hello!'"> 1 父类/布局模板: <html> ... <p th:text="${greeting}"></p> <!-- You'll end up with "Hello!" in here --> 将来,或许会增加支持使用分片局部变量,很像Thymeleaf用于创建片段签名。 配置标题 鉴于布局方言自动用内容模板中所发现的重载布局<title>,可能会发现自己重复布局中发现的标题部分,尤其是想要创建面包屑或者在页面标题中保留页面名称。layout:title-pattern处理器可以免除重复布局标题的问题,通过使用一些特殊标记以想要标题如何出现的模式。 这是一个例子: Layout.html <!DOCTYPE html> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <head> <title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">My website</title> </head> ... </html> layout:title-pattern处理器采取简单字符串,识别两种特殊标记:$LAYOUT_TITLE与$CONTENT_TITLE。每种标记在结果页中会被各自相应的标题替换。所以,如果有下面的内容模板: Content.html <!DOCTYPE html> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="Layout"> <head> <title>My blog</title> </head> ... </html> 结果页会是这样: <!DOCTYPE html> <html> <head> <title>My website - My blog</title> </head> ... </html> 这对<title>元素内的静态/内联文本或者<title>元素上发现使用th:text的动态文本均有效。 上述例子中的模式在布局中指定,所以对所有使用布局的内容模板均适用。如果在内容模板中指定另一种标题模式,那么会覆盖布局中发现的那个,允许细粒度的控制标题的展现形式。 可重用模板 假如发现有一些HTML或者结构经常性地重复,想要做成自己的模板从不同地方插入以便减少代码重复。(模块化Thymeleaf?)这个的例子可能会是一个模态面板,由几个HTML元素与CSS类构成,在网页应用中产生一个新窗口的效果: Modal.html <!DOCTYPE html> <html> <body> <div id="modal-container" class="modal-container" style="display:none;"> <section id="modal" class="modal"> <header> <h1>My Modal</h1> <div id="close-modal" class="modal-close"> <a href="#close">Close</a> </div> </header> <div id="modal-content" class="modal-content"> <p>My modal content</p> </div> </section> </div> </body> </html> 会发现可以将一些东西转换成像头部、ID的变量,以便包含Modal.html的页面可以设定它们自己的名称/ID。继续尽可能泛型化编写模态代码,然而会遇到填充自己的模态框内容的问题,那是开始接触一些限制的地方。 一些页面使用单一消息的模态框,其他想要使用模态框容纳一些更复杂的东西比如接受用户输入的表单。模态框可能性变得无休无止,但是未支持想象,发现自己得不得将这段模态框代码拷贝到每一个模板中,每一次使用场合变化相应内容,重复同样的HTML代码维持同样的外观感受,打破了过程中的DRY原则。 主要妨碍适当重用的事情是无法将HTML元素传递至插入模板中。这正是layout:insert有用的地方。它运作起来完全像th:insert,但是通过指定与实现片段很像内容/布局实例,可以创建一个公共的结构,对插入它的模板使用场合作出响应。 这是一个更新的模态框模板,使用Thymeleaf与layout:fragment属性定义一个可替换的模态框内容部分以变得更加泛型化: Modal2.html Modal2.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <body layout:fragment="modal(modalId, modalHeader)"> <div th:id="${modalId} + '-container'" class="modal-container" style="display:none;"> <section th:id="${modalId}" class="modal"> <header> <h1 th:text="${modalHeader}">My Modal</h1> <div th:id="'close-' + ${modalId}" class="modal-close"> <a href="#close">Close</a> </div> </header> <div th:id="${modalId} + '-content'" class="modal-content"> <div layout:fragment="modal-content"> <p>My modal content</p> </div> </div> </section> </div> </body> </html> 现在可以插入这个模板,使用layout:insert处理器与无论怎样需要实现modal-content片段,通过在调用模板插入元素内创建同样名称的片段: Content.html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> ... <div layout:insert="Modal2 :: modal(modalId='message', modalHeader='Message')" th:remove="tag"> <p layout:fragment="modal-content">Message goes here!</p> </div> ... </html> 就像内容/布局实例,插入模板layout:fragment会被匹配片段名称的元素替换掉。在这种场合下,Modal2.html的整个modal-content部分会被上述自定义段落替换掉。这是结果: <!DOCTYPE html> <html> ... <div id="message-container" class="modal-container" style="display:none;"> <section id="message" class="modal"> <header> <h1>Message</h1> <div id="close-message" class="modal-close"> <a href="#close">Close</a> </div> </header> <div id="message-content" class="modal-content"> <p>Message goes here!</p> </div> </section> </div> ... </html> 定义在模板内包含Modal2.html的自定义消息作为模态框内容的一部分。在插入模板上下文环境中的片段与用于内容/布局过程中的片段一样工作:如果片段未在模板中定义,那么它不会覆盖插入模板中的内容,使得在可重用版本中创建默认。

景凌凯 2020-04-29 21:11:25 0 浏览量 回答数 0

回答

编程规范先处理异常,后处理正常的######不要对多种情况进行处理, 慎记.######哦######+1, 正常往下处理,异常即时退出返回。######视情况而定###### 怎么写容易阅读就怎么写 ######我 也 这 样 觉 得######具体情况具体分析,代码最少就用哪个######我一般也是这样的习惯了,只是都会带上括号if(xxxx){xxxxx}######这是什么框架 ######自己实现的url路由呀.######没有使用框架######这是什么编辑器?######sublime text3 默认主题######sublime,用的是monokai主题!###### 引用来自“OscerSong”的答案 编程规范先处理异常,后处理正常的 +1 ######+1###### 个人觉得这段代码可以改改。 require $controller_path 这句,如果文件不存在,在这里就不行了。如果文件存在,但是并不能require,还是不行。就是说这句其实已经包含了上面file_exists的验证的,上面那句if多余的。 如果希望用自己的提示方式中断流程,那么可以这样写 include xxx || die(xxxxxx);  //die可以替换为某个log函数或输出函数 ###### 感觉show404这个也有点问题 既然是show404了,404是啥,http 404?控制器和act不存在也是404这语义吗? 而且这个show404抛出的页面是怎样的呢,既然文件和act不存在都用这个页面,那就不是精确的模板(指不是在上面写死“文件找不到”之类提示),而且调用内容是自己传,那就可以做个showErr或者showNotFound

kun坤 2020-06-09 10:54:58 0 浏览量 回答数 0

回答

Beego https://beego.me/ Beego 框架提供了很多标准附加功能,例如全功能路由器和可用于执行 CRUD 操作的对象到数据库映射工具。Bee 是 Beego 爱好者的最爱,它是一个快速而强大的命令行工具,用于构建、更新、打包和部署应用程序。Bee 可以从模板生成源代码,并保持数据库的最新状态。 Buffalo https://gobuffalo.io/en Buffalo 团队需要能够将 Web 应用程序的所有部分组装在一起的东西,包括应用程序本身的一些设计。他们把能够安装在一起的很多部件叫作“生态系统”。如果你想要路由——很少有人不需要——Buffalo 就包含了 Gorilla/Mux。如果你需要模板,Buffalo 倾向于使用 Plush,而不是使用内置的 Go 语言模板机制。数据库连接模块集合 Pop 可以帮你将数据库信息转换为 Go 对象。你还可以找到连接数据库、处理 cookie 以及完成其他任务的标准方法。 Cobra https://github.com/spf13/cobra 有时候,你只需要一个命令行界面。Cobra 提供了 CLI 的所有标准功能,因此你不必浪费时间实现代码来查找 -h 或 -help 标志。如果你的微服务需要对具有大量标志和其他功能的命令行调用做出响应,那么可以考虑集成 Cobra。 Docker 你当然可以在办公室服务器小黑屋里的裸机上运行微服务,但越来越多的人将他们的代码捆绑在 Docker 容器中,并将容器发到云端。小型的包更容易处理大量不同的代码块,当你对微服务架构的愿景要求你创建很多小的独立代码块时,这将是一项有价值的服务。 值得一提的是,Docker 是用 Go 语言开发的,尽管在部署 Docker 容器时你可能永远不会想到这一点。Docker 社区版是开源的,所以如果有必要,你可以参与其中,但很可能你只是将 Docker 作为部署微服务的工具。Go 语言爱好者之所以想要记住 Docker 是用 Go 语言开发的这一事实,是因为无处不在的 Docker 有力地证明了他们对这门语言的支持。 Echo https://echo.labstack.com/ Echo 是一个极简框架,但它提供了很多最重要的组件。路由器可以将 URL 拆解,然后将拆解的各个部份转换为参数,因此你无需自行解析它们。然后,你可以混合使用身份验证、表单解析、压缩和合理性限制。你可以专注于从函数中返回正确的信息。 Errors https://github.com/juju/errors 有时候,API 的用户会传递错误的参数。你可以自己处理这些参数,也可以把它们创给 Errors,这是一个可以自动执行大部分跟踪的库,方便进行调试。当发生错误时,Errors 会使用注释来详细说明出错的地方和位置。 Gin https://github.com/gin-gonic/gin Gin 是 Martini( https://github.com/go-martini/martini )的下一代框架。可以说,Gin 抛弃了那些额外的东西,专注于提供最有用的部分。花费大量时间构建 Node.js 微服务的开发人员会感到宾至如归。你可以实例化一个对象,然后附加函数来处理特定的调用,这样就可以创建一个微服务。Gin 负责处理路由,而你的函数处理业务逻辑。如果不去考虑标点符号,它的代码甚至看起来有点像 Node.js 代码。 Ginkgo https://onsi.github.io/ginkgo/ 测试可能是微服务开发当中最具挑战性的事情。Ginkgo 通过行为驱动测试扩展了标准 Go 发行版的内置测试机制。Ginkgo 提供了一种高级机制,用于指定函数或服务应该产生哪些结果。结果通常使用 Ginkgo 提供的 Gomega 匹配器( http://onsi.github.io/gomega/ )进行评估,但如果你愿意,也可以使用不同的匹配器库。 Ginkgo 是一个全面的框架,提供了各种选项,用于设置测试数据、运行测试以及在事后释放测试数据。你只需要描述结果,然后让 Ginkgo 处理其他的事情。 Goa https://github.com/goadesign/goa 如果你是一个曾经使用 Ruby 和 Praxis 框架的开发人员,或者是一个欣赏设计语言的强大力量人,那么你会在 Goa 中找到很多你喜欢的东西。你本身不需要编写 Go 代码。你使用 Goa DSL 为 API 编写设计规范,然后 Goa 将其转换为可执行的 Go 代码。DSL 针对微服务 API 进行了优化,并强制你的设计遵循标准的架构。 Gorilla http://www.gorillatoolkit.org/ Gorilla 项目提供了一系列你需要的模块。Gorilla 的 Mux( http://www.gorillatoolkit.org/pkg/mux )路由器被很多其他框架使用,因为它太好用了。很多用户之所以使用 Gorilla,是因为 websocket( http://www.gorillatoolkit.org/pkg/websocket )。 Gotify https://github.com/gotify/server 同步一组微服务所面临的一个挑战是建立有效的消息传递节点。Gotify 是一个简单的服务器,用于发送和接收消息,将你的微服务集合与持续存储的消息组合在一起。最有用的部分可能是它的 Web 接口,可帮助开发者应对最令人头疼的调试问题。 Hugo https://github.com/gohugoio/hugo Hugo 是一种静态站点生成器,可以用这个框架构建的微服务并不多,但如果网站只有有限的重复查询答案时,这是一个值得考虑的选项。Hugo 一次生成答案,然后可以重复使用。如果你已经已 HTML 格式提供答案,那么 Hugo 会非常有用。 Kite https://github.com/koding/kite 如果你希望建立一个更加可控的服务群体,而不是通常的服务之间的互动,那么可以考虑一下 Kite。Kite 的目标是让微服务之间的通信协调变得更简单一些。来自 Kite 以外的 API 调用通过 websocket 进入,然后 Kite 使用更快、更低级别的套接字连接(基于 dnode)传递新消息。中间有一个叫作 Kontrol 的服务注册表和身份验证服务。如果你需要经常交换消息和协调很多的操作,那么在不同服务器之间添加这一层会让一切变得更快。 Logrus https://github.com/sirupsen/logrus 要跟踪 API 的流入和流出数据和可能产生的错误,通常需要将日志写入文件中。这个过程可以很简单,比如在一个打开的文件中写入一行行的数据,但通常使用完整的日志框架会更好。Logrus 提供了格式化程序来标准化你的日志输出,并让后续的自动化日志文件分析变得更容易。不要尝试自己开发日志代码,使用像 Logrus 这样的库会事半功倍。 Nano https://github.com/pasztorpisti/nano 构建一个微服务并不需要太多东西,Nano 就是一个极简主义的例子。它的实际代码不会超过 200 行,如果算上注释也只有 400 多行。你只需要几行代码就可以构建一个微服务——只包含处理请求所需的业务逻辑。这个框架还有一些其他不错的特性,例如与语言无关的 API 结构,这样你的 Go 代码就可以与使用其他语言开发的服务发生交互。它还提供了一个测试过程来,可以嵌入你的本地测试例程。简简单单,但却恰到好处。 Negroni https://github.com/urfave/negroni 有些人看完 Martini 后,决定走一条更简单的道路。他们剥离了路由器和其他一些东西,创建了 Negroni,这是一个非常小型的工具,除了处理标准文件、自定义请求、从基本错误中恢复以及保留日志之外,它不会做更多的工作。如果你想要额外的东西,可以自己加入。Negroni 团队也提供了一系列与可以与 Negroni 一起使用的小型项目。 Renderer https://github.com/thedevsaddam/renderer 在准备输出响应时,你需要获取数据并将其插入到模板中。Renderer 提供了各种输出格式(JSON、JSONP、XML、YAML、HTML、文件)和一个漂亮、快速和标准的模板引擎。 Revel https://revel.github.io/ Revel 借鉴了 Webpack 的一个简洁的特性,这让 Revel 看起来就像一个 IDE 一样,或者至少是 IDE 的一部分,每当你对代码做出更改,它会持续地重新构建你的项目。当你保存修改后,Revel 会检测到更改,然后就编译代码,如果没有编译错误,就启动应用程序。因此,Revel 服务器会自动部署修改的码——在桌面上进行开发时这项功能非常好用,或许对于生产环境代码部署来说也是有点诱人的。 这个框架本身功能齐全,它提供了模板、缓存、验证和过滤器。如果你正在构建很多微服务,它还提供了一个模块系统,让你可以在项目之间共享一些 MVC 组件。 Testify https://github.com/stretchr/testify 使用断言的最简单方法之一是使用 Testify,它是一个 Go 语言项目,还提供了模拟工具,用于快速测试大型微服务的各个部分。只需要几行代码就编写一些基本测试用例。 Tollbooth https://github.com/didip/tollbooth 在你发布 API 之后,当然希望来自世界各个角落的人都可以调用它。但当你的服务器发生熔断,或者你看一看为了获得弹性而为云托管账户支付的费用时,你可能会改变主意。Tollbooth 是一个用于限制传入请求的轻量级系统。限制前门的流量就等于减少了对管道中微服务或数据库的需求,让一切保持运行顺畅。 不使用框架 你只需从头开始编写 Go 代码,不需要导入任何依赖项或者实例化任何控制对象。使用 Go 语言创建微服务其实很容易,因为它已经内置了很多基本代码。这就是为什么只用几百行代码就可以构建出像 Nano 这样的框架。 监听套接字、解压缩 HTTP 请求等工作都是通过标准库完成的。虽然框架提供了一些额外的功能,但很多时候如果你需要一个非常基本的微服务,就不需要用到框架。太多的“附加功能”可能反而会妨碍你,而且 Go 开发人员可能会说,太多的依赖反而让 Go 语言变得更复杂。

有只黑白猫 2020-01-08 11:53:57 0 浏览量 回答数 0

问题

第三章 – 布局,块和模板:报错

kun坤 2020-06-06 16:14:05 0 浏览量 回答数 1

问题

函数计算

黄一刀 2020-04-04 03:07:45 80 浏览量 回答数 1

问题

SSH面试题

琴瑟 2019-12-01 21:46:22 3489 浏览量 回答数 0

回答

您可以在阿里云容器服务 Kubernetes 版中创建服务。本文介绍如何创建服务。 前提条件 创建一个 kubernetes 集群,参见创建 Kubernetes 集群。 背景信息 Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略,通常称为微服务。这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 来实现的。 在 Kubernetes 中,Pod 虽然拥有独立的 IP, 但 Pod 会快速地创建和删除,因此,通过 Pod 直接对外界提供服务不符合高可用的设计准则。通过 Service 这个抽象,Service 能够解耦 frontend(前端)和 backend(后端) 的关联,frontend 不用关心 backend 的具体实现,从而实现松耦合的微服务设计。 更多详细的原理,请参见 Kubernetes service。 步骤1 创建 deployment 登录容器服务管理控制台。 在 Kubernetes 菜单下,单击左侧导航栏中的应用 > 无状态,进入无状态(Deployment)页面。 单击页面右上角的使用模板创建。 模板创建应用 选择所需的集群和命名空间,选择样例模板或自定义,然后单击创建。 设置参数 本例中,示例模板是一个 nginx 的 deployment。 apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-basic labels: app: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 # replace it with your exactly <image_name:tags> ports: - containerPort: 80 ##需要在服务中暴露该端口 单击Kubernetes 控制台,进入 Kubernetes Dashboard,查看该 Deployment 的运行状态。 查看运行状态 步骤2 创建服务 在 Kubernetes 菜单下,单击左侧导航栏中的路由与负载均衡 > 服务,进入服务列表页面。 选择所需的集群和命名空间,单击页面右上角的创建。 创建服务 在弹出的创建服务对话框中,进行配置。 配置参数 名称:输入服务的名称,本例中为 nginx-svc。 类型:选择服务类型,即服务访问的方式。包括: 虚拟集群 IP :即 ClusterIP,指通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。 说明 您的服务类型为虚拟集群IP时,才能设置实例间发现服务(Headless Service)。 节点端口:即 NodePort,通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 : ,可以从集群的外部访问一个 NodePort 服务。 负载均衡:即 LoadBalancer,指阿里云提供的负载均衡服务(SLB),可选择公网访问或内部访问。阿里云负载均衡服务可以路由到 NodePort 服务和 ClusterIP 服务。 新建 SLB:您可以通过单击修改,修改 SLB 规格。 使用已有负载均衡: 您可以在已有的列表中选择 SLB 规格。 说明 负载均衡类型支持新建 SLB 和使用已有 SLB,且多个 Kubernetes Service 可以复用同一个 SLB,但是存在以下限制: 使用已有的负载均衡实例会强制覆盖已有监听。 Kubernetes 通过 Service 创建的 SLB 不能复用(会导致 SLB 被意外删除)。只能复用您手动在控制台(或调用 OpenAPI)创建的 SLB。 复用同一个 SLB 的多个 Service 不能有相同的前端监听端口,否则会造成端口冲突。 复用 SLB 时,监听的名字以及虚拟服务器组的名字被 Kubernetes 作为唯一标识符。请勿修改监听和虚拟服务器组的名字。 不支持跨集群复用 SLB。 关联:选择服务要绑定的后端对象,本例中是前面创建的 nginx-deployment-basic 。若不进行关联部署,则不会创建相关的 Endpoints 对象,您可自己进行绑定,参见 services-without-selectors。 外部流量策略:可选值为Local或Cluster。 说明 您的服务类型为节点端口或负载均衡时,才能设置外部流量策略。 端口映射:添加服务端口和容器端口,容器端口需要与后端的 Pod 中暴露的容器端口一致。 注解:为该服务添加一个注解(annotation),配置负载均衡的参数,例如设置service.beta.kubernetes.io/alicloud-loadbalancer-bandwidth:20表示将该服务的带宽峰值设置为 20Mbit/s,从而控制服务的流量。更多参数请参见通过负载均衡(Server Load Balancer)访问服务。 标签:您可为该服务添加一个标签,标识该服务。 单击创建,nginx-svc 服务出现在服务列表中。 服务列表 您可查看服务的基本信息,在浏览器中访问 nginx-svc 的外部端点。 查看外部端点 至此,您已成功创建了一个关联到后端的 deployment 的服务,最后成功访问 Nginx 的欢迎页面。

1934890530796658 2020-03-31 15:18:24 0 浏览量 回答数 0

回答

MVC MVC模式最初生根于服务器端的Web开发,后来渐渐能够胜任客户端Web开发,能够满足其复杂性和丰富性。 这里写图片描述 MVC是Model-View-Controller的缩写,它将应用程序划分为三个部分: Model: 模型(用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法) View: 视图(渲染页面) Controller: 控制器(M和V之间的连接器,用于控制应用程序的流程,及页面的业务逻辑) MVC特点: MVC模式的特点在于实现关注点分离,即应用程序中的数据模型与业务和展示逻辑解耦。在客户端web开发中,就是将模型(M-数据、操作数据)、视图(V-显示数据的HTML元素)之间实现代码分离,松散耦合,使之成为一个更容易开发、维护和测试的客户端应用程序。 View 传送指令到 Controller ; Controller 完成业务逻辑后,要求 Model 改变状态 ; Model 将新的数据发送到 View,用户得到反馈。 MVC流程: MVC流程一共有两种,在日常开发中都会使用到。 一种是通过 View 接受指令,传递给 Controller,然后对模型进行修改或者查找底层数据,最后把改动渲染在视图上。 这里写图片描述 另一种是通过controller接受指令,传给Controller: 这里写图片描述 MVC优点: 耦合性低,视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码。 重用性高 生命周期成本低 MVC使开发和维护用户接口的技术含量降低 可维护性高,分离视图层和业务逻辑层也使得WEB应用更易于维护和修改 部署快 MVC缺点: 不适合小型,中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。 视图与控制器间过于紧密连接,视图与控制器是相互分离,但却是联系紧密的部件,视图没有控制器的存在,其应用是很有限的,反之亦然,这样就妨碍了他们的独立重用。 视图对模型数据的低效率访问,依据模型操作接口的不同,视图可能需要多次调用才能获得足够的显示数据。对未变化数据的不必要的频繁访问,也将损害操作性能。 MVC应用: 在web app 流行之初, MVC 就应用在了java(struts2)和C#(ASP.NET)服务端应用中,后来在客户端应用程序中,基于MVC模式,AngularJS应运而生。 MVP MVP(Model-View-Presenter)是MVC的改良模式,由IBM的子公司Taligent提出。和MVC的相同之处在于:Controller/Presenter负责业务逻辑,Model管理数据,View负责显示只不过是将 Controller 改名为 Presenter,同时改变了通信方向。 MVP特点: M、V、P之间双向通信。 View 与 Model 不通信,都通过 Presenter 传递。Presenter完全把Model和View进行了分离,主要的程序逻辑在Presenter里实现。 View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。 Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以保持Presenter的不变,这样就可以重用。不仅如此,还可以编写测试用的View,模拟用户的各种操作,从而实现对Presenter的测试–从而不需要使用自动化的测试工具。 与MVC区别: 这里写图片描述 在MVP中,View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部。 这里写图片描述 在MVC中,View会直接从Model中读取数据而不是通过 Controller。 MVP优点: 模型与视图完全分离,我们可以修改视图而不影响模型; 可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部; 我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁; 如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。 MVP缺点: 视图和Presenter的交互会过于频繁,使得他们的联系过于紧密。也就是说,一旦视图变更了,presenter也要变更。 MVP应用: 可应用与Android开发。 MVVM MVVM是Model-View-ViewModel的简写。微软的WPF(Windows Presentation Foundation–微软推出的基于Windows 的用户界面框架)带来了新的技术体验, 使得软件UI层更加细节化、可定制化。与此同时,在技术层面,WPF也带来了 诸如Binding(绑定)、Dependency Property(依赖属性)、Routed Events(路由事件)、Command(命令)、DataTemplate(数据模板)、ControlTemplate(控制模板)等新特性。MVVM模式其实是MV模式与WPF结合的应用方式时发展演变过来的一种新型架构模式。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。 这里写图片描述 MVVM优点: MVVM模式和MVC模式类似,主要目的是分离视图(View)和模型(Model),有几大优点: 低耦合,视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。 可重用性,可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。 独立开发,开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。 可测试,界面向来是比较难于测试的,而现在测试可以针对ViewModel来写。

景凌凯 2020-04-06 21:07:31 0 浏览量 回答数 0

回答

本文适用基于API URL发起HTTP/HTTPS GET请求的用户,如果您使用的是SDK可以跳过此环节。 发起API请求的URL由不同参数拼凑而成,有固定的请求结构。URL中包含公共参数、您的签名机制和某个API的具体参数。每篇API文档均给出了URL请求示例供您参考,但是为了方便显示,我们并没有编码这些URL示例,您需要在发起请求前自行编码。我们根据您的签名验证了请求后,会返回结果。接口调用成功会显示返回参数,调用失败则显示相应报错,您可以根据公共错误码和具体API错误码分析排查。 公共参数分为公共请求参数和公共返回参数。 公共请求参数 名称 类型 是否必需 描述 Action String 是 API的名称。取值参见API概览。 AccessKeyId String 是 访问密钥ID。AccessKey用于调用API,而用户密码用于登录OOS 管理控制台 Signature String 是 您的签名。取值参见签名机制。 SignatureMethod String 是 签名方式。取值范围:HMAC-SHA1。 SignatureVersion String 是 签名算法版本。取值范围:1.0。 SignatureNonce String 是 签名唯一随机数。用于防止网络重放攻击,建议您每一次请求都使用不同的随机数。 Timestamp String 是 请求的时间戳。按照时间格式标准表示,并需要使用UTC时间,格式为yyyy-MM-ddTHH:mm:ssZ。示例:2019-06-01T12:00:00Z表示北京时间2019年06月01日12点00分00秒。 Version String 是 API 的版本号,格式为 YYYY-MM-DD。取值范围:2019-06-01。 Format String 是 返回参数的语言类型。取值范围:json。 请求示例 https://oos.cn-hangzhou.aliyuncs.com/?Action=XXXXXX &Format=json &Version=2019-06-01 &Signature=cWgxtKc9dQWgSMB7OEhtOkq%2B9AM%3D &SignatureMethod=HMAC-SHA1 &SignatureNonce=479a2bf5-8045-11e9-8d42-6c96cfdd1fa1 &SignatureVersion=1.0 &AccessKeyId=key-test &Timestamp=2019-05-27T06%3A04%3A25Z … 公共返回参数 名称 类型 描述 RequestId String 请求ID。无论调用接口成功与否,我们都会返回请求ID。 阿里云OOS API支持基于URL发起HTTP/HTTPS GET请求。请求参数需要包含在URL中。本文列举了GET请求中的结构解释,并提供了OOS的服务接入地址(Endpoint)。 结构示例 以下为CreateTemplate一条未编码的URL请求示例: https://oos.cn-hangzhou.aliyuncs.com/?Action=CreateTemplate &TemplateName=YourTemplateName &<公共请求参数> https指定了请求通信协议。 oos.cn-hangzhou.aliyuncs.com指定了OOS的服务接入地址(Endpoint)。 Action=CreateTemplate指定了要调用的API,TemplateName=YourTemplateName是CreateTemplate规定的参数。 <公共请求参数>是系统规定的公共参数。 通信协议 支持HTTP或HTTPS协议请求通信。为了获得更高的安全性,推荐您使用HTTPS协议发送请求。 涉及敏感数据时,如用户密码和SSH密钥对,推荐使用HTTPS协议。例如,在StartExecution中指定敏感参数时。 接入地址 OOS API的服务接入地址,参见以下表格。为较少网络延时,建议您根据业务调用来源配置接入地址。 华东1(杭州) oos.cn-hangzhou.aliyuncs.com 华北2(北京) oos.cn-beijing.aliyuncs.com 华北5(呼和浩特) oos.cn-huhehaote.aliyuncs.com 华北3(张家口) oos.cn-zhangjiakou.aliyuncs.com 中国香港 oos.cn-hongkong.aliyuncs.com 华东2(上海) oos.cn-shanghai.aliyuncs.com 华南1(深圳) oos.cn-shenzhen.aliyuncs.com 印度(孟买) oos.ap-south-1.aliyuncs.com 美国(弗吉尼亚) oos.us-east-1.aliyuncs.com 德国(法兰克福) oos.eu-central-1.aliyuncs.com 英国(伦敦) oos.eu-west-1.aliyuncs.com 请求参数 您需要通过Action参数指定目标操作,例如Action=CreateTemplate。还需要指定接口的其他参数以及公共请求参数。 字符编码 请求及返回结果都使用UTF-8字符集编码。 对于每一次HTTP或者HTTPS协议请求,我们会根据访问中的签名信息验证访问请求者身份。具体由使用AccessKeyID和AccessKeySecret对称加密验证实现。 AccessKey相当于用户密码,AccessKey用于调用API,而用户密码用于登录OOS控制台。其中AccessKeyID是访问者身份,AccessKeySecret是加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密谨防泄露。 说明 我们提供了多种编程语言的SDK及第三方SDK,可以免去您签名的烦恼。更多详情,请下载SDK。 构造规范化请求字符串 排序参数。排序规则以首字母顺序排序,排序参数包括公共请求参数和接口自定义参数,不包括公共请求参数中的Signature参数。 说明 当使用GET方法提交请求时,这些参数就是请求URL中的参数部分,即URL中?之后由&连接的部分。 编码参数。使用UTF-8字符集按照RFC3986规则编码请求参数和参数取值,编码规则如下: 字符A~Z、a~z、0~9以及字符 -、_、.、~不编码。 其它字符编码成%XY的格式,其中XY是字符对应ASCII码的16进制。示例:半角双引号(")对应%22。 扩展的UTF-8字符,编码成%XY%ZA…的格式。 空格( )编码成%20,而不是加号(+)。该编码方式与application/x-www-form-urlencoded MIME格式编码算法相似,但又有所不同。如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号(+)替换为%20、星号()替换为%2A、%7E替换为波浪号(~),即可得到上述规则描述的编码字符串。 private static final String ENCODING = "UTF-8"; private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("", "%2A").replace("%7E", "~") : null; } 使用等号(=)连接编码后的请求参数和参数取值。 使用与号(&)连接编码后的请求参数,注意参数排序与步骤一:构造规范化请求字符串一致。 现在,您得到了规范化请求字符串(CanonicalizedQueryString),其结构遵循请求结构。 构造签名字符串 构造待签名字符串StringToSign。您可以同样使用percentEncode处理上一步构造的规范化请求字符串,规则如下: StringToSign= HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。 percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。 percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。 按照RFC2104的定义,计算待签名字符串StringToSign的HMAC-SHA1值。示例使用的是Java Base64编码方法。 Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) 添加根据RFC3986规则编码后的参数Signature到规范化请求字符串URL中。 说明 计算签名时,RFC2104规定的Key值是您的AccessKeySecret并加上与号(&),其ASCII值为38。 示例 1. 参数拼接法 以调用ListTemplates查询模板为例。假设您获得了AccessKeyID=testid以及AccessKeySecret=testsecret,签名流程如下所示: 构造规范化请求字符串。 AccessKeyId=testid&Action=ListTemplates&Format=json&SignatureMethod=HMAC-SHA1&SignatureNonce=9a3fdf30-8049-11e9-8875-6c96cfdd1fa1&SignatureVersion=1.0&Timestamp=2019-05-27T06%3A35%3A22Z&Version=2019-06-01 构造待签名字符串StringToSign,详情请参见步骤二:构造签名字符串。 GET&%2F&AccessKeyId%3Dtestid&Action%3DListTemplates&Format%3Djson&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D9a3fdf30-8049-11e9-8875-6c96cfdd1fa1&SignatureVersion%3D1.0&Timestamp%3D2019-05-27T06%253A35%253A22Z&Version%3D2019-06-01 计算签名值。因为AccessKeySecret=testsecret,用于计算的Key为testsecret&,计算得到的签名值为 1FcsD6/AvH2KugeowoCJSi8lBd8=。示例使用的是Java Base64编码方法。 Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) 添加RFC3986规则编码后的Signature=1FcsD6%2FAvH2KugeowoCJSi8lBd8%3D到步骤一:构造规范化请求字符串的URL中。 http://oos.cn-hangzhou.aliyuncs.com/?SignatureVersion=1.0&Format=json&Timestamp=2019-05-27T06%3A35%3A22Z&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&Version=2019-06-01&Signature=1FcsD6%2FAvH2KugeowoCJSi8lBd8%3D&Action=ListTemplates&SignatureNonce=9a3fdf30-8049-11e9-8875-6c96cfdd1fa1 通过以上URL,您可以使用浏览器、curl或者wget等工具发起HTTP请求调用ListTemplates,查询您的模板。 示例 2. 编程语言法 依然以调用ListTemplates查询模板为例。假设您获得了AccessKeyID=testid以及AccessKeySecret=testsecret,并且假定所有请求参数放在一个Java Map<String, String>对象里。 预定义编码方法。 private static final String ENCODING = "UTF-8"; private static String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null; } 预定义编码时间格式Timestamp。参数Timestamp必须符合时间格式规范,并需要使用UTC时间,时区为+0。 private static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; private static String formatIso8601Date(Date date) { SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATE_FORMAT); df.setTimeZone(new SimpleTimeZone(0, "GMT")); return df.format(date); } 构造请求字符串。 final String HTTP_METHOD = "GET"; Map parameters = new HashMap(); // 输入请求参数 parameters.put("Action", "ListTemplates"); parameters.put("Version", "2019-06-01"); parameters.put("AccessKeyId", "testid"); parameters.put("Timestamp", formatIso8601Date(new Date())); parameters.put("SignatureMethod", "HMAC-SHA1"); parameters.put("SignatureVersion", "1.0"); parameters.put("SignatureNonce", UUID.randomUUID().toString()); parameters.put("Format", "JSON"); // 排序请求参数 String[] sortedKeys = parameters.keySet().toArray(new String[]{}); Arrays.sort(sortedKeys); final String SEPARATOR = "&"; // 构造 stringToSign 字符串 StringBuilder stringToSign = new StringBuilder(); stringToSign.append(HTTP_METHOD).append(SEPARATOR); stringToSign.append(percentEncode("/")).append(SEPARATOR); StringBuilder canonicalizedQueryString = new StringBuilder(); for(String key : sortedKeys) { // 这里注意编码 key 和 value canonicalizedQueryString.append("&") .append(percentEncode(key)).append("=") .append(percentEncode(parameters.get(key))); } // 这里注意编码 canonicalizedQueryString stringToSign.append(percentEncode( canonicalizedQueryString.toString().substring(1))); 签名。因为 AccessKeySecret=testsecret,所以用于计算HMAC的Key为testsecret&,计算得到的签名值为1FcsD6/AvH2KugeowoCJSi8lBd8=。 // 以下是一段计算签名的示例代码 final String ALGORITHM = "HmacSHA1"; final String ENCODING = "UTF-8"; key = "testsecret&"; Mac mac = Mac.getInstance(ALGORITHM); mac.init(new SecretKeySpec(key.getBytes(ENCODING), ALGORITHM)); byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING)); String signature = new String(Base64.encodeBase64(signData)); 增加签名参数后,按照RFC3986规则编码后的URL如下所示: http://oos.cn-hangzhou.aliyuncs.com/?SignatureVersion=1.0&Format=json&Timestamp=2019-05-27T06%3A35%3A22Z&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&Version=2019-06-01&Signature=1FcsD6%2FAvH2KugeowoCJSi8lBd8%3D&Action=ListTemplates&SignatureNonce=9a3fdf30-8049-11e9-8875-6c96cfdd1fa1 使用浏览器、curl或者wget等工具发送HTTP请求。 { "Templates": [ { "Hash": "9ea05673ea6c7d03fe8707ddd3d347d74f9e47963538097d120583c0b5385edf", "Description": "", "TemplateName": "demo", "ShareType": "Private", "Tags": [], "TemplateFormat": "JSON", "CreatedBy": "1000008528360000", "CreatedDate": "2019-06-01T12:00:00Z", "TemplateVersion": "v1", "TotalExecutionCount": 1 },...], "NextToken": "gAAAAABc5pnvl_meBuZkGEbZNmzTOA1RotEjFs4q8q2fICes_oGk4qenxGB5UeyXYWa5eF4TeZLQMpnVc6rmhr2BC0Kahyx1P5brt_fQvLFTLBEcXQzzqsWwxDS-Y1xL7pkhOOgU_K3Z0-dMQHOkIQwWhodp4qutzKNO7gQ-EPWQvM1ugrGkonFDe_bV3TckHyHqgJDTLwk8Swfm1NvnZTXg9AH8YLvhu0fTK8sgXI6nBkvqTkozedWFhq8jmH1SFbs1yVZoKoKpFaYV6-vuTFcUO9G9-5W0YVdfliTuEOf42QHtkWLZ8jj1Xhmk9rTBYjb7P4KmRm7b7UhQ6FamvpVv9vXlCQvWAafnPgT98ViFlr6z7ByNHrI=", "RequestId": "8B813042-7F00-45CE-AF8C-85CCEEFC6BA2", "MaxResults": 50 } 返回结果列举了模板信息。 返回结果为JSON格式。为了便于查看和美观,API文档返回示例均有换行和缩进等处理,实际返回结果无换行和缩进处理。 正常返回示例 接口调用成功后会返回接口返回参数和请求ID,我们称这样的返回为正常返回。HTTP状态码为2xx。 JSON示例 { "RequestId": "AC467B38-3910-447D-87BC-AC049166F216" /* 返回结果数据 */ } 异常返回示例 接口调用出错后,会返回错误码、错误信息和请求ID,我们称这样的返回为异常返回。HTTP状态码为4xx或者5xx。 您可以根据接口错误码,参考公共错误码以及API错误中心排查错误。当您无法排查错误时,可以提交工单联系我们,并在工单中注明服务节点HostId和RequestId。 JSON示例 { "RequestId": "A40CFF28-407A-40B5-B6A5-AC049166F216", /* 请求 ID / "HostId": "oos.cn-hangzhou.aliyuncs.com", / 服务节点 / "Code": "InvalidNextToken", / 错误码 / "Message": "NextToken is invalid" / 错误信息 / } 公共错误码 错误代码 HTTP状态码 错误信息 描述 InvalidVersion 400 Specified parameter Version is not valid. 给定的Version非法。请检查URL中指定的Version的正确性。 InvalidAction.NotFound 400 Specified api is not found, please check your url and method. 给定的API不存在。请检查URL中指定的Action的正确性。 Throttling.User 400 Request was denied due to user flow control. 访问频率太高导致流控。 InvalidParameter 400 内容随校验场景不同而不同,如 TemplateName can not contain “” 参数非法。请根据错误信息来检查指定参数的正确性。 User.NoPermission 400 User has no permission to do the action: ({api_name}) 用户没有调用某个API的权限。请检查是否在RAM中给当前用户赋予了OOS的API的权限。 InvalidStsToken 400 Invalid STS token to do the action: ({api_name}) 给定的STS Token非法。 ExpiredStsToken 400 Expired STS token to do the action: ({api_name}) 给定的STS Token过期。 MissingParameter 400 The Parameter ({name}) was not provided. 缺少必填参数。 QuotaExceed 400 The Quota ({key}) exceeded ({value}). 特定属性(如模板数量、正在运行的执行数等)超过上限。 InvalidAccountType 400 The account type ({account_type}) was not supported. 不支持给定的账号类型。 TemplateValidationError 400 {reason} 模板约束,如参数类型、数值等,校验不通过。 InvalidFunctionParameters 400 The specified function {fn_name}’s parameters are incorrect. 模板中使用函数时给定的参数校验错误。请检查参数的数量和值。 InvalidTemplateReference 400 The specified reference {resource} (in {key}) is incorrect. 模板内参数引用不正确。 UnknownUserParameter 400 The parameter ({key}) was not defined in engine. 模板中给定的参数未定义或不被支持。 InvalidTimerTriggerParameter 400 The parameter ({key}) is invalid TimerTrigger动作的给定参数非法。 InvalidTemplateParameter 400 The parameter ({key}) has no attributes. 模板中的Parameters类型非法,必须为字典。

1934890530796658 2020-03-24 11:32:55 0 浏览量 回答数 0

回答

本文介绍了如何使用任务步骤的等待回调(waitForCallback)模式集成 MNS 主题,并发布消息到主题。MNS 主题接收到消息后,调用工作流 ReportTaskSucceeded 或 ReportTaskFailed API 回调任务状态。 框架原理 应用部署后执行流程如下: 执行工作流,任务步骤发布消息到 MNS 主题。任务步骤的 TaskToken 放入消息体一起发送到主题。 工作流任务步骤暂停执行,等待任务回调。 MNS 主题接收到消息后,将消息和 TaskToken 通过 HTTP 推送发送到函数计算 FC 的函数 HTTP 触发器,触发函数执行。 函数计算函数最终获取到 TaskToken,并调用 ReportTaskSucceeded 来报告任务状态。 流程继续执行。 11 部署应用 在 Serverless 工作流控制台创建流程,选择示例项目、任务步骤编排 MNS 主题模板,单击下一步。 2 在创建应用页面,创建模板对应的应用,并单击部署。 3 其中: 应用名称:自定义参数,同一账号下必须唯一。 TopicName:自定义参数,如果对应 MNS 主题不存在会自动创建。 单击部署后,会显示应用下创建的所有资源。4 执行工作流。 执行输入 { "messageBody": "hello world" } 5 应用代码 编排 MNS 主题的工作流。 将任务步骤回调的 TaskToken 封装在消息的 MessageBody 中,用于后续的回调。outputMappings 中读取 ReportTaskSucceeded 设置的 output。 version: v1 type: flow steps: - type: task name: mns-topic-task resourceArn: acs:mns:::/topics/ /messages pattern: waitForCallback inputMappings: - target: messageBody source: $input.messageBody - target: taskToken source: $context.task.token outputMappings: - target: status source: $local.status serviceParams: MessageBody: $ 回调任务步骤的 FC 函数。 读取 MessageBody中封装的 TaskToken 回调任务状态设置 output 为 {"status":"success"}。 def handler(environ, start_response): # Get request body try: request_body_size = int(environ.get('CONTENT_LENGTH', 0)) except ValueError: request_body_size = 0 request_body = environ['wsgi.input'].read(request_body_size) print('Request body: {}'.format(request_body)) body = json.loads(request_body) message_body_str = body['Message'] # Read MessageBody and TaskToken from message body message_body = json.loads(message_body_str) task_token = message_body['taskToken'] ori_message_body = message_body['messageBody'] print('Task token: {}\norigin message body: {}'.format(task_token, ori_message_body)) # Init fnf client use sts token context = environ['fc.context'] creds = context.credentials sts_creds = StsTokenCredential(creds.access_key_id, creds.access_key_secret, creds.security_token) fnf_client = AcsClient(credential=sts_creds, region_id=context.region) # Report task succeeded to serverless workflow req = ReportTaskSucceededRequest() req.set_TaskToken(task_token) req.set_Output('{"status": "success"}') resp = fnf_client.do_action_with_exception(req) print('Report task response: {}'.format(resp)) # Response to http request status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [b'OK']

1934890530796658 2020-03-27 10:48:20 0 浏览量 回答数 0

问题

Netty实现原理浅析 1、总体结构 2、网络模型 3、 buffer 4、Ch?400报错

爱吃鱼的程序员 2020-06-04 11:53:36 3 浏览量 回答数 1

回答

本文主要为您介绍支持 GPU 调度的 Kubernetes GPU 集群。 前提条件 您需要开通容器服务和访问控制(RAM)服务。 登录 容器服务管理控制台和RAM 管理控制台开通相应的服务。 背景信息 自从1.8版本开始,Kubernetes已经明确表示要通过统一的设备插件方式支持像Nvidia GPU,InfiniBand,FPGA 等硬件加速设备,而社区的GPU方案将在1.10全面弃用,并在1.11版本彻底从主干代码移除。若您需要通过阿里云Kubernetes集群+GPU运行机器学习,图像处理等高运算密度等任务,无需安装nvidia driver和CUDA,就能实现一键部署和弹性扩缩容等功能。 创建集群过程中,容器服务会进行如下操作: 创建 ECS,配置管理节点到其他节点的 SSH 的公钥登录,通过 CloudInit 安装配置 Kubernetes 集群。 创建安全组,该安全组允许 VPC 入方向全部 ICMP 端口的访问。 如果您不使用已有的 VPC 网络,会为您创建一个新的 VPC 及 VSwitch,同时为该 VSwitch 创建 SNAT。 创建 VPC 路由规则。 创建 NAT 网关及 EIP。 创建 RAM 子账号和 AK,该子账号拥有 ECS 的查询、实例创建和删除的权限,添加和删除云盘的权限,SLB 的全部权限,云监控的全部权限,VPC 的全部权限,日志服务的全部权限,NAS 的全部权限。Kubernetes 集群会根据用户部署的配置相应的动态创建 SLB,云盘,VPC路由规则。 创建内网 SLB,暴露 6443 端口。 创建公网 SLB,暴露 6443、8443和 22 端口(如果您在创建集群的时候选择开放公网 SSH 登录,则会暴露 22 端口;如果您选择不开放公网 SSH 访问,则不会暴露 22 端口)。 使用限制 用户账户需有 100 元的余额并通过实名认证,否则无法创建按量付费的 ECS 实例和负载均衡。 随集群一同创建的负载均衡实例只支持按量付费的方式。 Kubernetes 集群仅支持专有网络 VPC。 每个账号默认可以创建的云资源有一定的配额,如果超过配额创建集群会失败。请在创建集群前确认您的配额。如果您需要提高配额,请提交工单申请。 每个账号默认最多可以创建 50 个集群(所有地域下),每个集群中最多可以添加 100 个节点。如果您需要创建更多的集群或者节点,请提交工单申请。 说明 Kubernetes 集群中,VPC 默认路由条目不超过 48 条,意味着 Kubernetes 集群使用 VPC 时,默认路由条目上限是 48 个,如果需要更大的路由条目数,需要您先对目标 VPC 提交工单,申请提高配额。 每个账号默认最多可以创建 100 个安全组。 每个账号默认最多可以创建 60 个按量付费的负载均衡实例。 每个账号默认最多可以创建 20 个EIP。 ECS 实例使用限制: 支持创建按量付费和包年包月的 ECS 实例。 说明 实例创建后,您可以通过 ECS 管理控制台将按量付费转包年包月。 创建GN5型Kubernetes集群 登录容器服务管理控制台。 在 Kubernetes 菜单下,单击左侧导航栏的集群 > 集群,单击页面右上角的创建 Kubernetes 集群。 在选择集群模板页面,选择标准专有版集群页面,并单击创建,进入Kubernetes 专有版页面。 说明 为了创建GPU集群,通常情况下,Worker节点使用GPU类型的ECS。其他集群的参数配置,请参见创建 Kubernetes 集群。 设置 Worker 节点的配置信息。本例中将Worker节点作为GPU工作节点,选择GPU计算型gn5。 若您选择新增实例,则需要选择 Worker 节点的系列和规格,以及需要创建的 Worker 节点的数量(本示例创建2个GPU节点)。 节点设置 若您选择添加已有实例,则需要预先在此地域下创建GPU云服务器。 完成其他配置后,单击创建集群,启动部署。 集群创建成功后,单击左侧导航栏中的集群 > 节点,进入节点列表页面。 选择所需的集群,选择创建集群时配置的Worker节点,单击操作列的更多 > 详情,查看该节点挂载的GPU设备。 运行TensorFLow的GPU实验环境 数据科学家通常习惯使用Jupyter作为TensorFlow实验环境,我们这里可以用一个例子向您展示如何快速部署一个Jupyter应用。 在 Kubernetes 菜单下,单击左侧导航栏的应用 > 无状态,进入无状态(Deployment)页面。 单击页面右上角的创建使用模板创建 。 选择所需的集群,命名空间,选择样例模板或自定义,然后单击创建。 创建应用 本例中,示例模板是一个Jupyter应用,包括一个deployment和service。 Define the tensorflow deployment apiVersion: apps/v1 kind: Deployment metadata: name: tf-notebook labels: app: tf-notebook spec: replicas: 1 selector: # define how the deployment finds the pods it mangages matchLabels: app: tf-notebook template: # define the pods specifications metadata: labels: app: tf-notebook spec: containers: - name: tf-notebook image: tensorflow/tensorflow:1.4.1-gpu-py3 resources: limits: nvidia.com/gpu: 1 #指定调用nvidia gpu的数量 ports: - containerPort: 8888 hostPort: 8888 env: - name: PASSWORD # 指定访问Jupyter服务的密码,您可以按照您的需要修改 value: mypassw0rd Define the tensorflow service apiVersion: v1 kind: Service metadata: name: tf-notebook spec: ports: - port: 80 targetPort: 8888 name: jupyter selector: app: tf-notebook type: LoadBalancer #阿里云的负载均衡访问内部服务和负载均衡 旧的GPU部署方案,您必须要定义如下的nvidia驱动所在的数据卷。 volumes: - hostPath: path: /usr/lib/nvidia-375/bin name: bin - hostPath: path: /usr/lib/nvidia-375 name: lib 这需要您在编写部署文件时,强依赖于所在的集群,导致缺乏可移植性。但是在Kubernetes 1.9.3及之后的版本中,最终用户无需指定这些hostPath,nvidia的插件会自发现驱动所需的库链接和执行文件。 单击左侧导航栏中的路由与负载均衡 > 服务,选择所需的集群和命名空间,选择tf-notebook服务,查看外部端点。 查看服务 在浏览器中访问Jupyter实例,访问地址是http://EXTERNAL-IP,输入模板中配置的密码。 您可通过如下的程序,验证这个Jupyter实例可以使用GPU。它将列出Tensorflow可用的所有设备。 from tensorflow.python.client import device_lib def get_available_devices(): local_device_protos = device_lib.list_local_devices() return [x.name for x in local_device_protos] print(get_available_devices()) 查看结果

1934890530796658 2020-03-27 10:03:01 0 浏览量 回答数 0

问题

【精品问答】媒体处理

montos 2020-04-08 19:12:05 4 浏览量 回答数 1

问题

如何用DMS创建存储过程

云栖大讲堂 2019-12-01 21:29:19 1379 浏览量 回答数 0

问题

HttpUtilities API使用方法

猫饭先生 2019-12-01 21:25:46 1190 浏览量 回答数 0

回答

重试作用: 对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。 远程调用超时、网络突然中断可以重试。在微服务治理框架中,通常都有自己的重试与超时配置,比如dubbo可以设置retries=1,timeout=500调用失败只重试1次,超过500ms调用仍未返回则调用失败。 比如外部 RPC 调用,或者数据入库等操作,如果一次操作失败,可以进行多次重试,提高调用成功的可能性。 优雅的重试机制要具备几点: 无侵入:这个好理解,不改动当前的业务逻辑,对于需要重试的地方,可以很简单的实现 可配置:包括重试次数,重试的间隔时间,是否使用异步方式等 通用性:最好是无改动(或者很小改动)的支持绝大部分的场景,拿过来直接可用 优雅重试共性和原理: 正常和重试优雅解耦,重试断言条件实例或逻辑异常实例是两者沟通的媒介。 约定重试间隔,差异性重试策略,设置重试超时时间,进一步保证重试有效性以及重试流程稳定性。 都使用了命令设计模式,通过委托重试对象完成相应的逻辑操作,同时内部封装实现重试逻辑。 Spring-tryer和guava-tryer工具都是线程安全的重试,能够支持并发业务场景的重试逻辑正确性。 优雅重试适用场景: 功能逻辑中存在不稳定依赖场景,需要使用重试获取预期结果或者尝试重新执行逻辑不立即结束。比如远程接口访问,数据加载访问,数据上传校验等等。 对于异常场景存在需要重试场景,同时希望把正常逻辑和重试逻辑解耦。 对于需要基于数据媒介交互,希望通过重试轮询检测执行逻辑场景也可以考虑重试方案。 优雅重试解决思路: 切面方式 这个思路比较清晰,在需要添加重试的方法上添加一个用于重试的自定义注解,然后在切面中实现重试的逻辑,主要的配置参数则根据注解中的选项来初始化 优点: 真正的无侵入 缺点: 某些方法无法被切面拦截的场景无法覆盖(如spring-aop无法切私有方法,final方法) 直接使用aspecj则有些小复杂;如果用spring-aop,则只能切被spring容器管理的bean 消息总线方式 这个也比较容易理解,在需要重试的方法中,发送一个消息,并将业务逻辑作为回调方法传入;由一个订阅了重试消息的consumer来执行重试的业务逻辑 优点: 重试机制不受任何限制,即在任何地方你都可以使用 利用EventBus框架,可以非常容易把框架搭起来 缺点: 业务侵入,需要在重试的业务处,主动发起一条重试消息 调试理解复杂(消息总线方式的最大优点和缺点,就是过于灵活了,你可能都不知道什么地方处理这个消息,特别是新的童鞋来维护这段代码时) 如果要获取返回结果,不太好处理, 上下文参数不好处理 模板方式 优点: 简单(依赖简单:引入一个类就可以了; 使用简单:实现抽象类,讲业务逻辑填充即可;) 灵活(这个是真正的灵活了,你想怎么干都可以,完全由你控制) 缺点: 强侵入 代码臃肿 把这个单独捞出来,主要是某些时候我就一两个地方要用到重试,简单的实现下就好了,也没有必用用到上面这么重的方式;而且我希望可以针对代码快进行重试 这个的设计还是非常简单的,基本上代码都可以直接贴出来,一目了然: 复制代码 public abstract class RetryTemplate { private static final int DEFAULT_RETRY_TIME = 1; private int retryTime = DEFAULT_RETRY_TIME; private int sleepTime = 0;// 重试的睡眠时间 public int getSleepTime() { return sleepTime; } public RetryTemplate setSleepTime(int sleepTime) { if(sleepTime < 0) { throw new IllegalArgumentException("sleepTime should equal or bigger than 0"); } this.sleepTime = sleepTime; return this; } public int getRetryTime() { return retryTime; } public RetryTemplate setRetryTime(int retryTime) { if (retryTime <= 0) { throw new IllegalArgumentException("retryTime should bigger than 0"); } this.retryTime = retryTime; return this; } /** * 重试的业务执行代码 * 失败时请抛出一个异常 * * todo 确定返回的封装类,根据返回结果的状态来判定是否需要重试 * * @return */ protected abstract Object doBiz() throws Exception; //预留一个doBiz方法由业务方来实现,在其中书写需要重试的业务代码,然后执行即可 public Object execute() throws InterruptedException { for (int i = 0; i < retryTime; i++) { try { return doBiz(); } catch (Exception e) { log.error("业务执行出现异常,e: {}", e); Thread.sleep(sleepTime); } } return null; } public Object submit(ExecutorService executorService) { if (executorService == null) { throw new IllegalArgumentException("please choose executorService!"); } return executorService.submit((Callable) () -> execute()); } } 复制代码 使用示例: 复制代码 public void retryDemo() throws InterruptedException { Object ans = new RetryTemplate() { @Override protected Object doBiz() throws Exception { int temp = (int) (Math.random() * 10); System.out.println(temp); if (temp > 3) { throw new Exception("generate value bigger then 3! need retry"); } return temp; } }.setRetryTime(10).setSleepTime(10).execute(); System.out.println(ans); } 复制代码 spring-retry Spring Retry 为 Spring 应用程序提供了声明性重试支持。 它用于Spring批处理、Spring集成、Apache Hadoop(等等)的Spring。 在分布式系统中,为了保证数据分布式事务的强一致性,在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。 用的最多的重试方式就是MQ了,但是如果你的项目中没有引入MQ,就不方便了。 还有一种方式,是开发者自己编写重试机制,但是大多不够优雅。 缺陷 spring-retry 工具虽能优雅实现重试,但是存在两个不友好设计: 一个是重试实体限定为 Throwable 子类,说明重试针对的是可捕捉的功能异常为设计前提的,但是我们希望依赖某个数据对象实体作为重试实体, 但 sping-retry框架必须强制转换为Throwable子类。 另一个是重试根源的断言对象使用的是 doWithRetry 的 Exception 异常实例,不符合正常内部断言的返回设计。 Spring Retry 提倡以注解的方式对方法进行重试,重试逻辑是同步执行的,当抛出相关异常后执行重试, 如果你要以返回值的某个状态来判定是否需要重试,可能只能通过自己判断返回值然后显式抛出异常了。只读操作可以重试,幂等写操作可以重试,但是非幂等写操作不能重试,重试可能导致脏写,或产生重复数据。 @Recover 注解在使用时无法指定方法,如果一个类中多个重试方法,就会很麻烦。 spring-retry 结构 BackOff:补偿值,一般指失败后多久进行重试的延迟值。 Sleeper:暂停应用的工具,通常用来应用补偿值。 RetryState:重试状态,通常包含一个重试的键值。 RetryCallback:封装你需要重试的业务逻辑(上文中的doSth) RecoverCallback:封装了多次重试都失败后你需要执行的业务逻辑(上文中的doSthWhenStillFail) RetryContext:重试语境下的上下文,代表了能被重试动作使用的资源。可用于在多次Retry或者Retry 和Recover之间传递参数或状态(在多次doSth或者doSth与doSthWhenStillFail之间传递参数) RetryOperations: 定义了“重试”的模板(重试的API),要求传入RetryCallback,可选传入RecoveryCallback; RetryTemplate :RetryOperations的具体实现,组合了RetryListener[],BackOffPolicy,RetryPolicy。 RetryListener:用来监控Retry的执行情况,并生成统计信息。 RetryPolicy:重试的策略或条件,可以简单的进行多次重试,可以是指定超时时间进行重试(上文中的someCondition),决定失败能否重试。 BackOffPolicy: 重试的回退策略,在业务逻辑执行发生异常时。如果需要重试,我们可能需要等一段时间(可能服务器过于繁忙,如果一直不间隔重试可能拖垮服务器),当然这段时间可以是0,也可以是固定的,可以是随机的(参见tcp的拥塞控制算法中的回退策略)。回退策略在上文中体现为wait(); RetryPolicy提供了如下策略实现: NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试; AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环; SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略; TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试; CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate delegate:是真正判断是否重试的策略,当重试失败时,则执行熔断策略;应该配置基于次数的SimpleRetryPolicy或者基于超时的TimeoutRetryPolicy策略,且策略都是全局模式,而非局部模式,所以要注意次数或超时的配置合理性。 openTimeout:openWindow,配置熔断器电路打开的超时时间,当超过openTimeout之后熔断器电路变成半打开状态(主要有一次重试成功,则闭合电路); resetTimeout:timeout,配置重置熔断器重新闭合的超时时间 CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许重试即可以,悲观组合重试策略是指只要有一个策略不允许重试即可以,但不管哪种组合方式,组合中的每一个策略都会执行。 BackOffPolicy 提供了如下策略实现: NoBackOffPolicy:无退避算法策略,即当重试时是立即重试; FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper(指定等待策略,默认是Thread.sleep,即线程休眠)、backOffPeriod(休眠时间,默认1秒); UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod、maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒; ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier。initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier; ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数,固定乘数可能会引起很多服务同时重试导致DDos,使用随机休眠时间来避免这种情况。 RetryTemplate主要流程实现: 复制代码 //示例一 public void upload(final Map<String, Object> map) throws Exception { // 构建重试模板实例 RetryTemplate retryTemplate = new RetryTemplate(); // 设置重试策略,主要设置重试次数 SimpleRetryPolicy policy =         new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)); // 设置重试回退操作策略,主要设置重试间隔时间 FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); fixedBackOffPolicy.setBackOffPeriod(100); retryTemplate.setRetryPolicy(policy); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); // 通过RetryCallback 重试回调实例包装正常逻辑逻辑,第一次执行和重试执行执行的都是这段逻辑 final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() { //RetryContext 重试操作上下文约定,统一spring-try包装 public Object doWithRetry(RetryContext context) throws Exception { System.out.println("do some thing"); Exception e = uploadToOdps(map); System.out.println(context.getRetryCount()); throw e;//这个点特别注意,重试的根源通过Exception返回 } }; // 通过RecoveryCallback 重试流程正常结束或者达到重试上限后的退出恢复操作实例 final RecoveryCallback recoveryCallback = new RecoveryCallback() { public Object recover(RetryContext context) throws Exception { System.out.println("do recory operation"); return null; } }; try { // 由retryTemplate 执行execute方法开始逻辑执行 retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } } //示例二 protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,RecoveryCallback recoveryCallback,   RetryState state) throws E, ExhaustedRetryException { //重试策略 RetryPolicy retryPolicy = this.retryPolicy; //退避策略 BackOffPolicy backOffPolicy = this.backOffPolicy; //重试上下文,当前重试次数等都记录在上下文中 RetryContext context = open(retryPolicy, state); try { //拦截器模式,执行RetryListener#open boolean running = doOpenInterceptors(retryCallback, context); //判断是否可以重试执行 while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { try {//执行RetryCallback回调 return retryCallback.doWithRetry(context); } catch (Throwable e) {//异常时,要进行下一次重试准备 //遇到异常后,注册该异常的失败次数 registerThrowable(retryPolicy, state, context, e); //执行RetryListener#onError doOnErrorInterceptors(retryCallback, context, e); //如果可以重试,执行退避算法,比如休眠一小段时间后再重试 if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) { backOffPolicy.backOff(backOffContext); } //state != null && state.rollbackFor(context.getLastThrowable()) //在有状态重试时,如果是需要执行回滚操作的异常,则立即抛出异常 if (shouldRethrow(retryPolicy, context, state)) { throw RetryTemplate. wrapIfNecessary(e); } } //如果是有状态重试,且有GLOBAL_STATE属性,则立即跳出重试终止;       //当抛出的异常是非需要执行回滚操作的异常时,才会执行到此处,CircuitBreakerRetryPolicy会在此跳出循环; if (state != null && context.hasAttribute(GLOBAL_STATE)) { break; } } //重试失败后,如果有RecoveryCallback,则执行此回调,否则抛出异常 return handleRetryExhausted(recoveryCallback, context, state); } catch (Throwable e) { throw RetryTemplate. wrapIfNecessary(e); } finally { //清理环境 close(retryPolicy, context, state, lastException == null || exhausted); //执行RetryListener#close,比如统计重试信息 doCloseInterceptors(retryCallback, context, lastException); } } 复制代码 有状态or无状态 无状态重试,是在一个循环中执行完重试策略,即重试上下文保持在一个线程上下文中,在一次调用中进行完整的重试策略判断。如远程调用某个查询方法时是最常见的无状态重试: 复制代码 RetryTemplate template = new RetryTemplate(); //重试策略:次数重试策略 RetryPolicy retryPolicy = new SimpleRetryPolicy(3); template.setRetryPolicy(retryPolicy); //退避策略:指数退避策略 ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); backOffPolicy.setInitialInterval(100); backOffPolicy.setMaxInterval(3000); backOffPolicy.setMultiplier(2); backOffPolicy.setSleeper(new ThreadWaitSleeper()); template.setBackOffPolicy(backOffPolicy); //当重试失败后,抛出异常 String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { throw new RuntimeException("timeout"); } }); //当重试失败后,执行RecoveryCallback String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new RuntimeException("timeout"); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }); 复制代码 有状态重试,有两种情况需要使用有状态重试,事务操作需要回滚、熔断器模式。 事务操作需要回滚场景时,当整个操作中抛出的是数据库异常DataAccessException,则不能进行重试需要回滚,而抛出其他异常则可以进行重试,可以通过RetryState实现: 复制代码 //当前状态的名称,当把状态放入缓存时,通过该key查询获取 Object key = "mykey"; //是否每次都重新生成上下文还是从缓存中查询,即全局模式(如熔断器策略时从缓存中查询) boolean isForceRefresh = true; //对DataAccessException进行回滚 BinaryExceptionClassifier rollbackClassifier = new BinaryExceptionClassifier(Collections.<Class<? extends Throwable>>singleton(DataAccessException.class)); RetryState state = new DefaultRetryState(key, isForceRefresh, rollbackClassifier); String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new TypeMismatchDataAccessException(""); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }, state); 复制代码 RetryTemplate中在有状态重试时,回滚场景时直接抛出异常处理代码: //state != null && state.rollbackFor(context.getLastThrowable()) //在有状态重试时,如果是需要执行回滚操作的异常,则立即抛出异常 if (shouldRethrow(retryPolicy,context, state)) { throw RetryTemplate. wrapIfNecessary(e); } 熔断器场景。在有状态重试时,且是全局模式,不在当前循环中处理重试,而是全局重试模式(不是线程上下文),如熔断器策略时测试代码如下所示。 复制代码 RetryTemplate template = new RetryTemplate(); CircuitBreakerRetryPolicy retryPolicy = new CircuitBreakerRetryPolicy(new SimpleRetryPolicy(3)); retryPolicy.setOpenTimeout(5000); retryPolicy.setResetTimeout(20000); template.setRetryPolicy(retryPolicy); for (int i = 0; i < 10; i++) { try { Object key = "circuit"; boolean isForceRefresh = false; RetryState state = new DefaultRetryState(key, isForceRefresh); String result = template.execute(new RetryCallback<String, RuntimeException>() { @Override public String doWithRetry(RetryContext context) throws RuntimeException { System.out.println("retry count:" + context.getRetryCount()); throw new RuntimeException("timeout"); } }, new RecoveryCallback () { @Override public String recover(RetryContext context) throws Exception { return "default"; } }, state); System.out.println(result); } catch (Exception e) { System.out.println(e); } } 复制代码 为什么说是全局模式呢?我们配置了isForceRefresh为false,则在获取上下文时是根据key “circuit”从缓存中获取,从而拿到同一个上下文。 Object key = "circuit"; boolean isForceRefresh = false; RetryState state = new DefaultRetryState(key,isForceRefresh); 如下RetryTemplate代码说明在有状态模式下,不会在循环中进行重试。 if (state != null && context.hasAttribute(GLOBAL_STATE)) { break; } 判断熔断器电路是否打开的代码: 复制代码 public boolean isOpen() { long time = System.currentTimeMillis() - this.start; boolean retryable = this.policy.canRetry(this.context); if (!retryable) {//重试失败 //在重置熔断器超时后,熔断器器电路闭合,重置上下文 if (time > this.timeout) { this.context = createDelegateContext(policy, getParent()); this.start = System.currentTimeMillis(); retryable = this.policy.canRetry(this.context); } else if (time < this.openWindow) { //当在熔断器打开状态时,熔断器电路打开,立即熔断 if ((Boolean) getAttribute(CIRCUIT_OPEN) == false) { setAttribute(CIRCUIT_OPEN, true); } this.start = System.currentTimeMillis(); return true; } } else {//重试成功 //在熔断器电路半打开状态时,断路器电路闭合,重置上下文 if (time > this.openWindow) { this.start = System.currentTimeMillis(); this.context = createDelegateContext(policy, getParent()); } } setAttribute(CIRCUIT_OPEN, !retryable); return !retryable; } 复制代码 从如上代码可看出spring-retry的熔断策略相对简单: 当重试失败,且在熔断器打开时间窗口[0,openWindow) 内,立即熔断; 当重试失败,且在指定超时时间后(>timeout),熔断器电路重新闭合; 在熔断器半打开状态[openWindow, timeout] 时,只要重试成功则重置上下文,断路器闭合。 注解介绍 @EnableRetry 表示是否开始重试。 序号 属性 类型 默认值 说明 1 proxyTargetClass boolean false 指示是否要创建基于子类的(CGLIB)代理,而不是创建标准的基于Java接口的代理。当proxyTargetClass属性为true时,使用CGLIB代理。默认使用标准JAVA注解 @Retryable 标注此注解的方法在发生异常时会进行重试 序号 属性 类型 默认值 说明 1 interceptor String ”” 将 interceptor 的 bean 名称应用到 retryable() 2 value class[] {} 可重试的异常类型 3 include class[] {} 和value一样,默认空,当exclude也为空时,所有异常都重试 4 exclude class[] {} 指定异常不重试,默认空,当include也为空时,所有异常都重试 5 label String ”” 统计报告的唯一标签。如果没有提供,调用者可以选择忽略它,或者提供默认值。 6 maxAttempts int 3 尝试的最大次数(包括第一次失败),默认为3次。 7 backoff @Backoff @Backoff() 重试补偿机制,指定用于重试此操作的backoff属性。默认为空 @Backoff 不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms 序号 属性 类型 默认值 说明 1 delay long 0 指定延迟后重试 ,如果不设置则默认使用 1000 milliseconds 2 maxDelay long 0 最大重试等待时间 3 multiplier long 0 指定延迟的倍数,比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为10秒,第三次为20秒(大于0生效) 4 random boolean false 随机重试等待时间 @Recover 用于恢复处理程序的方法调用的注释。返回类型必须与@retryable方法匹配。 可抛出的第一个参数是可选的(但是没有它的方法只会被调用)。 从失败方法的参数列表按顺序填充后续的参数。 用于@Retryable重试失败后处理方法,此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。 说明: 使用了@Retryable的方法不能在本类被调用,不然重试机制不会生效。也就是要标记为@Service,然后在其它类使用@Autowired注入或者@Bean去实例才能生效。 要触发@Recover方法,那么在@Retryable方法上不能有返回值,只能是void才能生效。 使用了@Retryable的方法里面不能使用try...catch包裹,要在发放上抛出异常,不然不会触发。 在重试期间这个方法是同步的,如果使用类似Spring Cloud这种框架的熔断机制时,可以结合重试机制来重试后返回结果。 Spring Retry不只能注入方式去实现,还可以通过API的方式实现,类似熔断处理的机制就基于API方式实现会比较宽松。 转载于:https://www.cnblogs.com/whatarewords/p/10656514.html

养狐狸的猫 2019-12-02 02:11:54 0 浏览量 回答数 0

回答

本文主要为您介绍如何部署有依赖关系的wordpress应用。 前提条件 创建一个 Kubernetes 集群。详情参见快速创建Kubernetes集群。 您需要创建存储卷和存储卷声明。关于如何创建存储卷请参见云盘存储卷使用说明、NAS存储卷使用说明、OSS存储卷使用说明;如何创建存储卷声明请参见创建持久化存储卷声明。本文示例中使用阿里云云盘作为存储卷,选择PV/PVC的方式进行存储卷挂载。在这里创建wordpress-pv-claim和wordpress-mysql-pv-claim两个存储声明,分别会在wordpress和wordpress-mysql的yaml文件中使用这两个声明来挂载相应的存储卷。 存储与存储声明 背景信息 本例主要演示如何通过编排模板中的自定义模板创建有依赖关系的应用。 主要组件有: wordpress mysql 涉及到的资源: 存储卷管理 secret管理 服务 操作步骤 登录容器服务管理控制台。 使用准备好的存储卷声明。在这里创建wordpress-pvc和wordpress-mysql-pvc两个存储声明,分别在wordpress和wordpress-mysql的yaml文件中,用这两个声明挂载相应的存储卷。 单击左侧导航栏中应用配置 > 保密字典,选择所需的集群和命名空间,单击右上角创建。创建过程请参考创建密钥。 创建密钥 由于创建和访问mysql数据库需要用户名密码,所以我们通过创建密钥的方式进行用户名密码的管理。 在使用secret前,您需要先将需要加密的secret在保密字典里进行创建,在本例中通过将mysql root的密码作为密钥进行创建,创建名称为mysql-pass,类型选择Opaque。该密钥会在后面的wordpress和wordpress-mysql的yaml文件中用到。 配置密钥页面 单击左侧导航栏中的应用 > 无状态,单击右上角使用模板创建。 创建应用 选择所需的集群和命名空间,创建wordpress deployment的yaml文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: wordpress labels: app: wordpress spec: selector: matchLabels: app: wordpress tier: frontend strategy: type: Recreate template: metadata: labels: app: wordpress tier: frontend spec: containers: - image: wordpress:4 name: wordpress env: - name: WORDPRESS_DB_HOST value: wordpress-mysql #通过名称指向需要访问的mysql,该名称与mysql service的名称相对应。 - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysql-pass key: password-wordpress ports: - containerPort: 80 name: wordpress volumeMounts: - name: wordpress-pvc mountPath: /var/www/html volumes: - name: wordpress-pvc persistentVolumeClaim: claimName: wordpress-pv-claim 创建mysql deployment的yaml文件如下: apiVersion: apps/v1 kind: Deployment metadata: name: wordpress-mysql labels: app: wordpress spec: selector: matchLabels: app: wordpress tier: mysql strategy: type: Recreate template: metadata: labels: app: wordpress tier: mysql spec: containers: - image: mysql:5.6 name: mysql env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-pass key: password-mysql ports: - containerPort: 3306 name: mysql volumeMounts: - name: wordpress-mysql-pvc mountPath: /var/lib/mysql volumes: - name: wordpress-mysql-pvc persistentVolumeClaim: claimName: wordpress-mysql-pv-claim 为了使wordpress能够被外部访问,我们需要为wordpress创建service对外暴露访问方式,在这里使用LoadBalancer类型进行wordpress service的创建,容器服务会自动创建阿里云负载均衡,为用户提供外部访问。 wordpress mysql需要创建名为wordpress-mysql的service,以使在上面创建的wordpress deploymet可以访问到。由于该mysql只为wordpress内部调用,所以不需要为其创建LoadBalancer类型的service。 创建service的方法请参考创建服务。 创建wordpress和mysql service的yaml文件如下: apiVersion: v1 kind: Service metadata: name: wordpress labels: app: wordpress spec: ports: - port: 80 selector: app: wordpress tier: frontend type: LoadBalancer apiVersion: v1 kind: Service metadata: name: wordpress-mysql labels: app: wordpress spec: ports: - port: 3306 selector: app: wordpress tier: mysql clusterIP: None 当部署完成后,单击左侧导航栏中的路由与负载均衡 > 服务,找到wordpress服务并查看其外端端点。 服务列表 在浏览器中输入XX.XX.XX.XX/wp-admin/install.php,访问wordpress服务的外部端点,您就可以通过负载均衡提供的IP地址进行wordpress应用的访问。 此处的XX.XX.XX.XX为上步骤获取的外部端点的IP地址。 访问外部端点 后续步骤 在wordpress应用的配置过程中,您可以使用密钥中配置的密码登录应用,此外,wordpress应用所属的容器产生的数据会保存数据卷中。

1934890530796658 2020-03-26 11:41:24 0 浏览量 回答数 0

回答

本教程示例详细演示了如何控制用户对 OSS 存储空间和文件夹的访问。在示例中,我们首先创建一个存储空间和文件夹,然后使用阿里云主账号创建访问管理 (RAM) 用户,并为这些用户授予对所创建 OSS 存储空间及文件夹的增量权限。 存储空间和文件夹的基本概念 阿里云 OSS 的数据模型为扁平型结构,所有文件都直接隶属于其对应的存储空间。因此,OSS 缺少文件系统中类似于目录与子文件夹的层次结构。但是,您可以在 OSS 控制台上模拟文件夹层次结构。在该控制台中,您可以按文件夹对相关文件进行分组、分类和管理,如下图所示。     OSS 提供使用键值(key)对格式的分布式对象存储服务。用户根据其唯一的key(对象名)检索对象的内容。例如,名为 example-company 的存储空间有三个文件夹:Development、 Marketing 和 Private,以及一个对象 oss-dg.pdf。 在创建   Development  文件夹时,控制台会创建一个key为 Development/的对象。注意,文件夹的key包括分隔符   /。 当您将名为ProjectA.docx的对象上传到Development文件夹中时,控制台会上传该对象并将其key设置为Development/ProjectA.docx。 在该key中,Development为前缀,而/为分隔符。您可以从存储空间中获取具有特定前缀和分隔符的所有对象的列表。在控制台中,单击Development文件夹时,控制台会列出文件夹中的对象,如下图所示。 说明  当控制台列出   example-company存储空间中的   Development文件夹时,它会向 OSS 发送一个用于指定前缀   Development和分隔符   /的请求。控制台的响应与文件系统类似,会显示文件夹列表。上例说明,存储空间 example-company有三个对象,其key分别为   Development/Alibaba Cloud.pdf、 Development/ProjectA.docx及   Development/ProjectB.docx。 控制台通过对象的key推断逻辑层次结构。当您创建对象的逻辑层次结构时,您可以管理对个别文件夹的访问,如本教程后面描述的那样。 在本教程开始之前,您还需要知道“根级”存储空间内容的概念。假设example-company存储空间包含以下对象: Development/Alibaba Cloud.pdf Development/ProjectA.docx Development/ProjectB.docx Marketing/data2016.xlsx Marketing/data2016.xlsx Private/2017/images.zip Private/2017/promote.pptx oss-dg.pdf 这些对象的key构建了一个以 Development、Marketing 和 Private 作为根级文件夹并以 oss-dg.pdf 作为根级对象的逻辑层次结构。当您单击 OSS 控制台中的存储空间名时,控制台会将一级前缀和一个分隔符(Development/、Marketing/ 和 Private/)显示为根级文件夹。对象 oss-dg.pdf 没有前缀,因此显示为根级别项。   OSS 的请求和响应逻辑 在授予权限之前,我们需要清楚,当用户单击某个存储空间的名字时控制台向 OSS 发送的是什么请求、OSS 返回的是什么响应,以及控制台如何解析该响应。 当用户单击某个存储空间名时,控制台会将GetBucket 请求发送至 OSS。此请求包括以下参数: prefix,其值为空字符串。 delimiter,其值为 /。 请求示例如下所示: GET /?prefix=&delimiter=/ HTTP/1.1 Host: example-company.oss-cn-hangzhou.aliyuncs.com Date: Fri, 24 Feb 2012 08:43:27 GMT Authorization: OSS qn6qrrqxo2oawuk53otfjbyc:DNrnx7xHk3sgysx7I8U9I9IY1vY= OSS 返回的响应包括ListBucketResult元素: HTTP/1.1 200 OK x-oss-request-id: 534B371674E88A4D8906008B Date: Fri, 24 Feb 2012 08:43:27 GMT Content-Type: application/xml Content-Length: 712 Connection: keep-alive Server: AliyunOSS <?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns=¡±http://doc.oss-cn-hangzhou.aliyuncs.com¡±><Name>example-company</Name><Prefix></Prefix><Marker></Marker><MaxKeys>100</MaxKeys><Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <Contents> <Key>oss-dg.pdf</Key> ... </Contents> <CommonPrefixes> <Prefix>Development</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>Marketing</Prefix> </CommonPrefixes> <CommonPrefixes> <Prefix>Private</Prefix> </CommonPrefixes></ListBucketResult> 由于 oss-dg.pdf 不包含/分隔符,因此 OSS 在<Contents/>元素中返回该key。存储空间 example-company 中的所有其他key都包含/分隔符,因此 OSS 会将这些key分组,并为每个前缀值 Development/、Marketing/ 和 Private/ 返回一个<CommonPrefixes/>元素。该元素是一个字符串,包含从这些key的第一个字符开始到第一次出现指定的/分隔符之间的字符。 控制台会解析此结果并显示如下的根级别项:   现在,如果用户单击Development文件夹,控制台会将GetBucket请求发送至 OSS。此请求包括以下参数: prefix,其值为   Development/。 delimiter,其值为   /。 请求示例如下所示: GET /?prefix=Development/&delimiter=/ HTTP/1.1 Host: oss-example.oss-cn-hangzhou.aliyuncs.com Date: Fri, 24 Feb 2012 08:43:27 GMT Authorization: OSS qn6qrrqxo2oawuk53otfjbyc:DNrnx7xHk3sgysx7I8U9I9IY1vY= 作为响应,OSS 返回以指定前缀开头的key: HTTP/1.1 200 OK x-oss-request-id: 534B371674E88A4D8906008B Date: Fri, 24 Feb 2012 08:43:27 GMT Content-Type: application/xml Content-Length: 712 Connection: keep-alive Server: AliyunOSS <?xml version="1.0" encoding="UTF-8"?><ListBucketResult xmlns=¡±http://doc.oss-cn-hangzhou.aliyuncs.com¡±><Name>example-company</Name><Prefix>Development/</Prefix><Marker></Marker><MaxKeys>100</MaxKeys><Delimiter>/</Delimiter> <IsTruncated>false</IsTruncated> <Contents> <Key>ProjectA.docx</Key> ... </Contents> <Contents> <Key>ProjectB.docx</Key> ... </Contents></ListBucketResult> 控制台会解析此结果并显示如下的key:   教程示例 本教程示例如下所示: 创建一个存储空间example-company,然后向其中添加三个文件夹(Development、Marketing 和 Private)。 您有Anne和Leo两个用户。您希望Anne只能访问 Development文件夹而Leo则只能访问 Marketing文件夹,并且希望将 Private文件夹保持私有。在教程示例中,通过创建访问控制 (RAM) 用户(Anne和Leo)来管理访问权限,并授予他们必要的权限。 RAM 还支持创建用户组并授予适用于组中所有用户的组级别权限。这有助于更好地管理权限。在本示例中,Anne和Leo都需要一些公共权限。因此,您还要创建一个名为Staff 的组,然后将Anne和Leo添加到该组中。首先,您需要给该组分配策略授予权限。然后,将策略分配给特定用户,添加特定用户的权限。 说明  本教程示例使用example-company作为存储空间名、使用Anne和Leo作为RAM用户名并使用Staff作为组名。由于阿里云OSS要求存储空间名全局唯一,所以您需要用自己的存储空间名称替换本教程中的存储空间名。 示例准备 本示例使用阿里云主账号创建RAM用户。最初,这些用户没有任何权限。您将逐步授予这些用户执行特定 OSS 操作的权限。为了测试这些权限,您需要使用每个用户的RAM账号登录到控制台。当您作为主账号所有者逐步授予权限并作为RAM用户测试权限时,您需要每次使用不同账号进行登录和注销。您可以使用一个浏览器来执行此测试。如果您可以使用两个不同的浏览器,则该测试过程用时将会缩短:一个浏览器用于使用主账号连接到阿里云控制台,另一个浏览器用于使用RAM 账号进行连接。 要使用您的主账号登录到阿里云控制台。 RAM用户不能使用相同的链接登录。他们必须使用RAM用户登录链接。作为主账号所有者,您可以向RAM用户提供此链接。 说明  有关 RAM 的详细信息,请参见 使用 RAM 用户账号登录。 为 RAM 用户提供登录链接 使用主账号登录 RAM 控制台。 在左侧导航栏中,单击 概览。 在 RAM用户登录链接 后找到URL。您将向RAM用户提供此URL,以便其使用RAM用户名和密码登录控制台。 步骤 1:创建存储空间 在此步骤中,您可以使用主账号登录到OSS控制台、创建存储空间、将文件夹(Development、Marketing、Private)添加到存储空间中,并在每个文件夹中上传一个或两个示例文档。 使用主账号登录 OSS 控制台。 创建名为 example-company  的存储空间。 有关详细过程,请参见OSS 控制台用户指南中的创建存储空间 。 将一个文件上传到存储空间中。 本示例假设您将文件 oss-dg.pdf 上传到存储空间的根级别。您可以用不同的文件名上传自己的文件。 有关详细过程,请参见 OSS 控制台用户指南中的上传文件。 添加名为 Development、Marketing 和 Private 的三个文件夹。 有关详细过程,请参见 OSS 控制台用户指南 中的创建文件夹。 将一个或两个文件上传到每个文件夹中。 本例假设您将具有以下对象键的对象上传到存储空间中: Development/Alibaba Cloud.pdf Development/ProjectA.docx Development/ProjectB.docx Marketing/data2016.xlsx Marketing/data2016.xlsx Private/2017/images.zip Private/2017/promote.pptx oss-dg.pdf 步骤 2:创建 RAM 用户和组 在此步骤中,您使用 RAM 控制台将两个 RAM 用户 Anne 和 Leo 添加到主账号中。您还将创建一个名为 Staff 的组,然后将这两个用户添加到该组中。 说明  在此步骤中,不要分配任何授予这些用户权限的策略。在以下步骤中,您将逐步为其授予权限。 有关创建 RAM 用户的详细过程,请参见 RAM 快速入门中的创建 RAM 用户。请为每个 RAM 用户创建登录密码。 有关创建组的详细过程,请参见 RAM 用户指南中的创建组。 步骤 3:确认 RAM 用户没有任何权限 如果您使用两个浏览器,现在可以在另一个浏览器中使用其中一个 RAM 用户账号登录到控制台。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 您发现控制台中没有任何存储空间,这意味着 Anne 不具有对存储空间 example-company 的任何权限。 步骤 4:授予组级别权限 我们希望 Anne 和 Leo 都能执行以下操作: 列出主账号所拥有的所有存储空间。 为此,Anne 和 Leo 必须具有执行 oss:ListBuckets 操作的权限。 列出 example-company 存储空间中的根级别项、文件夹和对象。 为此,Anne 和 Leo 必须具有对 example-company 存储空间执行 oss:ListObjects 操作的权限。 步骤 4.1.授予列出所有存储空间的权限 在此步骤中,创建一个授予用户最低权限的策略。凭借最低权限,用户可列出主账号所拥有的所有存储空间。您还将此策略分配给 Staff 组,以便授予获得主账号拥有的存储空间列表的组权限。 使用主账号登录 RAM 控制台。 创建策略   AllowGroupToSeeBucketListInConsole。 在左侧导航窗格中,单击 策略管理,然后单击 新建授权策略。 单击 空白模板。 在 授权策略名称 字段中,输入 AllowGroupToSeeBucketListInConsole。 在 策略内容 字段中,复制并粘贴以下策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets" ], "Resource": [ "acs:oss:*:*:*" ] } ] } 说明  策略为 JSON 文档。在该策略中,Statement 是一个对象数组,每个对象使用名/值对的集合来描述权限。前面的策略描述了一个特定的权限。Effect 元素值决定是允许还是拒绝特定的权限。Action 指定访问权限的类型。在本策略中,oss:ListBuckets 是预定义的 OSS 操作,可返回经过身份验证的发送者所拥有的所有储存空间的列表。 将   AllowGroupToSeeBucketListInConsole  策略分配给 Staff 组。 有关分配策略的详细过程,请参见 RAM 快速入门 中将策略分配给 RAM 用户的将策略分配给 RAM 组。 可以将策略分配给 RAM 控制台中的 RAM 用户和组。在本例中,我们将策略分配给组,因为我们希望 Anne 和 Leo 都能够列出这些存储空间。 测试权限。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡。 此时将显示一个消息框,表明您没有相应的访问权限。 步骤 4.2.授予列出存储空间根级内容的权限 在此步骤中,您授予权限,允许所有用户列出存储空间 example-company 中的所有项目。当用户在 OSS 控制台中单击 example-company 时,能够看到存储空间中的根级别项。   使用主账号登录 RAM 控制台。 用以下策略取代分配给 Staff 组的现有策略 AllowGroupToSeeBucketListInConsole,该策略还允许 oss:ListObjects 操作。请用您的存储空间名替换策略资源中的 example-company。 有关详细过程,请参见 RAM 用户指南中授权策略的修改自定义授权策略部分。注意,您最多可对 RAM 策略进行五次修改。如果超过了五次,则需要删除该策略并创建一个新的策略,然后再次将新策略分配给 Staff 组。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets", "oss:GetBucketAcl" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": {} }, { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "" ], "oss:Delimiter": [ "/" ] } } } ] } 说明 要列出存储空间内容,用户需要调用 oss:ListObjects 操作的权限。为了确保用户仅看到根级内容,我们添加了一个条件:用户必须在请求中指定一个空前缀,也就是说,他们不能单击任何根级文件夹。我们还通过要求用户请求包含分隔符参数和值   /  来添加需要文件夹样式访问的条件。 当用户登录到 OSS 控制台时,控制台检查用户的身份是否有访问 OSS 服务的权限。要在控制台中支持存储空间操作,我们还需要添加 oss:GetBucketAcl 操作。 测试更新的权限。 打开 RAM 用户登录链接,并用 Anne 或 Leo 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡。 控制台列出所有根级别项。 单击任何文件夹或对象 oss-dg.pdf。 此时将显示一个消息框,表明您没有相应的访问权限。 组策略摘要 添加组策略的最终结果是授予 RAM 用户 Anne 和 Leo 以下最低权限: 列出主账号所拥有的所有存储空间。 查看 example-company 存储空间中的根级别项。 然而,他们可以进行的操作仍然有限。在以下部分中,我们将授予用户以下特定权限: 允许 Anne 在 Development 文件夹中获取和放入对象。 允许 Bob 在 Finance 文件夹中获取和放入对象。 对于用户特定的权限,您需要将策略分配给特定用户,而非分配给组。以下部分授予 Anne 在 Development 文件夹中操作的权限。您可以重复这些步骤,授予 Leo 在 Finance 文件夹中进行类似操作的权限。 步骤 5:授予 RAM 用户 Anne 特定权限 在此步骤中,我们向 Anne 授予额外的权限,使她可以看到 Development 文件夹的内容,并将对象放入文件夹中。 步骤 5.1.授予 RAM 用户 Anne 权限以列出 Development 文件夹内容 若要 Anne 能够列出 Development 文件夹内容,您必须为其分配策略。该策略必须能够授予其对 example-company 存储空间执行 oss:ListObjects 操作的权限,还必须包括要求用户在请求中指定前缀 Development/ 的条件。 使用主账号登录 RAM 控制台。 创建策略 AllowListBucketsIfSpecificPrefixIsIncluded,授予 RAM 用户 Anne 权限以列出 Development 文件夹内容。 在左侧导航窗格中,单击 策略管理,然后单击 新建授权策略。 单击 空白模板。 在 授权策略名称字段中,输入 AllowListBucketsIfSpecificPrefixIsIncluded。 在 策略内容 字段中,复制并粘贴以下策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } } ] } 将策略分配给 RAM 用户 Anne。 有关分配策略的详细过程,请参见 RAM 快速入门中的将策略分配给 RAM 用户 测试 Anne 的权限。 打开 RAM 用户登录链接,并用 Anne 的账号登录到 RAM 控制台。 打开 OSS 控制台,控制台列出所有存储空间。 单击 example-company 存储空间,然后单击 文件选项卡,控制台列出所有根级别项。 单击 Development/ 文件夹。控制台列出文件夹中的对象。 步骤 5.2 授予 RAM 用户 Anne 在 Development 文件夹中获取和放入对象的权限。 若要 Anne 能够在 Development 文件夹中获取和放入对象,您必须授予她调用 oss:GetObject 和 oss:PutObject 操作的权限,包括用户必须在请求中指定前缀 Development/ 的条件。 使用主账号登录   RAM 控制台。 用以下策略取代您在之前步骤中创建的策略 AllowListBucketsIfSpecificPrefixIsIncluded。 有关详细过程,请参见 RAM 用户指南中授权策略的修改自定义授权策略部分。注意,您最多可对 RAM 策略进行五次修改。如果超过了五次,则需要删除该策略并创建一个新的策略,然后再次将新策略分配给 Staff 组。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } }, { "Effect": "Allow", "Action": [ "oss:GetObject", "oss:PutObject", "oss:GetObjectAcl" ], "Resource": [ "acs:oss:*:*:example-company/Development/*" ], "Condition": {} } ] } 说明  当用户登录到 OSS 控制台时,控制台检查用户的身份是否有访问 OSS 服务的权限。要在控制台中支持存储空间操作,我们还需要添加 oss:GetObjectAcl 操作。 测试更新的策略。 打开 RAM 用户登录链接,并用 Anne 的账号登录到 RAM 控制台。 打开 OSS 控制台。 控制台列出所有存储空间。 在 OSS 控制台中,确认 Anne 现在可以在 Development 文件夹中添加对象并下载对象。 步骤 5.3 显式拒绝 RAM 用户 Anne 访问存储空间中任何其他文件夹的权限 RAM 用户 Anne 现在可以在 example-company 存储空间中列出根级内容,并将对象放入 Development 文件夹中。如果要严格限制访问权限,您可以显式拒绝 Anne 对存储空间中任何其他文件夹的访问。如果有授予 Anne 访问存储空间中任何其他文件夹的其他策略,则此显式策略将替代这些权限。 您可以将以下语句添加到 RAM 用户 Anne 的策略   AllowListBucketsIfSpecificPrefixIsIncluded,以要求 Anne 发送到 OSS 的所有请求包含前缀参数,该参数的值可以是 Development/* 或空字符串。 { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringNotLike": { "oss:Prefix": [ "Development/*", "" ] } } } 按照之前的步骤更新您为 RAM 用户 Anne 创建的策略   AllowListBucketsIfSpecificPrefixIsIncluded。复制并粘贴以下策略以替换现有策略。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "Development/*" ] } } }, { "Effect": "Allow", "Action": [ "oss:GetObject", "oss:PutObject", "oss:GetObjectAcl" ], "Resource": [ "acs:oss:*:*:example-company/Development/*" ], "Condition": {} }, { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringNotLike": { "oss:Prefix": [ "Development/*", "" ] } } } ] } 步骤 6:授予 RAM 用户 Leo 特定权限 现在,您希望授予 Leo 访问 Marketing 文件夹的权限。请遵循之前用于向 Anne 授予权限的步骤,但应将 Development 文件夹替换为 Marketing 文件夹。有关详细过程,请参见步骤 5:授予 RAM 用户 Anne 特定权限。 步骤 7:确保 Private 文件夹安全 在本例中,您仅拥有两个用户。您在组级别授予两个用户所有所需的最小权限,只有当您真正需要单个用户级别上的权限时,才授予用户级别权限。此方法有助于最大限度地减少管理权限的工作量。随着用户数量的增加,我们希望确保不意外地授予用户对 Private 文件夹的权限。因此,我们需要添加一个显式拒绝访问 Private 文件夹的策略。显式拒绝策略会取代任何其他权限。若要确保 Private 文件夹保持私有,可以向组策略添加以下两个拒绝语句: 添加以下语句以显式拒绝对 Private 文件夹 (example-company/Private/*) 中的资源执行任何操作。 { "Effect": "Deny", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:example-company/Private/*" ], "Condition": {} } 您还要在请求指定了 Private/ prefix 时拒绝执行 ListObjects 操作的权限。在控制台中,如果 Anne 或 Leo 单击 Private 文件夹,则此策略将导致 OSS 返回错误响应。 { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": { "StringLike": { "oss:Prefix": [ "Private/" ] } } } 用包含前述拒绝语句的更新策略取代 Staff 组策略AllowGroupToSeeBucketListInConsole。在应用更新策略后,组中的任何用户都不能访问您的存储空间中的 Private 文件夹。 使用主账号登录 RAM 控制台。 用以下策略取代分配给 Staff 组的现有策略 AllowGroupToSeeBucketListInConsole。请用您的存储空间名替换策略资源中的 example-company。 { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": [ "oss:ListBuckets", "oss:GetBucketAcl" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": {} }, { "Effect": "Allow", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:example-company" ], "Condition": { "StringLike": { "oss:Prefix": [ "" ], "oss:Delimiter": [ "/" ] } } }, { "Effect": "Deny", "Action": [ "oss:*" ], "Resource": [ "acs:oss:*:*:example-company/Private/*" ], "Condition": {} }, { "Effect": "Deny", "Action": [ "oss:ListObjects" ], "Resource": [ "acs:oss:*:*:*" ], "Condition": { "StringLike": { "oss:Prefix": [ "Private/" ] } } } ] } 清理 要进行清理,您需要在 RAM 控制台中删除用户 Anne 和 Leo。 有关详细过程,请参见 RAM 用户指南中用户的删除RAM用户部分。 为了确保您不再因存储而继续被收取费用,您还需要删除为本示例创建的对象和存储空间。

51干警网 2019-12-02 00:38:21 0 浏览量 回答数 0

问题

如何使用函数计算

云栖大讲堂 2019-12-01 20:56:05 1691 浏览量 回答数 0

问题

Vue面试题汇总【精品问答】

问问小秘 2020-05-25 18:02:28 11132 浏览量 回答数 2
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 阿里云双十一主会场 阿里云双十一新人会场 1024程序员加油包 阿里云双十一拼团会场 场景化解决方案 阿里云双十一直播大厅