OpenStack 企业私有云的若干需求(3):多租户和租户间隔离(multi-tenancy and isolation)
本系列会介绍OpenStack 企业私有云的几个需求:
- 自动扩展(Auto-scaling)支持
- 多租户和租户隔离 (multi-tenancy and tenancy isolation)
- 混合云(Hybrid cloud)支持
- 主流硬件支持、云快速交付 和 SLA 保证
- 大规模扩展性支持
- 私有云外围环境支持(包括支持CDN 、商业SDN控制器、防火墙和VPN/专线等)
- 良好的可使用性(用户和运维 Dashboard 等)
- 向上扩展性(PaaS 和 SaaS 等支撑)
- 企业数据中心IT环境支持(包括裸金属/Bare metal、F5 、GPU、跨云网络连通、租户计费、备份等支持)
- 行业解决方案
- 独立的服务,包括培训、运维等
1. 基本概念
1.1 用户、租户与多租户
用户、租户与多租户是一个适用于多个行业的普遍性概念。
一个环境/系统的一个使用者即该环境/系统的一个用户。系统允许其用户通过一个登录流程(enrollment process)进入该系统,并获取访问系统及其资源的权限。用户在管理粒度上被分到若干组内,每组成为一个租户(tenant)。一个简单的层级中,一个用户只属于一个租户;但是,复杂情况下,一个租户还可以有自租户。因此,用户可以分为几类,包括管理员用户,租户管理员用户和普通租户用户等。
多租户(multi-tenancy)是指一个建立在共同的底层资源上的环境被被多个租户共同使用。就像一个大楼一样,许多租户共享大楼的基础设施,比如电梯,但是使用墙和门来在租户之间做隔离;在一个公司内部,敏感部门还需要进一步隔离。多租户的含义包括两个方面:(1)共享带来经济性 (2)隔离保证安全性。还是以一个办公楼举例子:
1.2 云环境的单租户和多租户
1.2.1 概念
(1)单租户
单租户(single tenancy)是指一个云环境只被一个租户的用户使用。
(2)多租户
多租户是云所使用的一种基础性技术,它通过共享硬件或软件来实现云的规模性成本和安全性。云使用多租户技术来在多个租户和应用直接安全地共享IT资源。实际上,多租户是在云系统在安全和成本之间的一种妥协(trade-off):底层资源共享的程度越高,云系统的运行成本就越低、资源利用率就越高;然后,这常常会导致隔离性(isolation)的进一步降低以及更高的安全风险(security risk)。
(3)各种类型云的租户性质
根据所服务的用户,云环境可以分为四大类:
# | 名称 | 说明 | 主要的租户模式 |
1 | 私有云 |
云基础设施只被一个企业的用户使用。它可能由该企业来管理(非托管式),或者由厂家或者第三方来管理(托管式); 它有可能部署在客户数据中心内(私有云)或者第三方数据中心内(专有云)。该企业即该云系统的租户。 |
单租户 |
2 | 公有云 | 由云提供商拥有,服务大众(public)。 | 多租户(共享式) |
3 | 混合云 |
云基础设施包括两个或者多个云(私有、社区或者公有云),各个云保持独立实体,但是它们被使用标准或者私有的技术捆绑在一起, 来实现数据和应用的可移植性。 |
N/A |
4 | 社区云 |
云基础设施被几个组织共享,支持一个有共同需求的社团(community),该社团内的企业有共同或者相似的愿景、安全性需求、 策略和规范性需求等。 |
多租户(共享 + 隔离) |
1.2.2 多租户
(1)Gartner 定义的多租户级别:第1到第7级
云服务提供商希望利用多租户带来的资源高度共享模式(架构),提高资源利用率,降低单位资源成本。但是,因为租户间隔离性下降,多租户技术会给租户带来一系列问题。
(来源)
其中,三种应用较广发的典型模式为:
- 1 - sharing nothing
- 2 - sharing hardware
- 6 - sharing everything
(2)公有云厂商的选择:因为有技术保证,因此往往选择较高级别,保证公有云的经济性。
(来源)
(3)企业用户的选择
企业在选择公共云服务或者自建私有云时,到底需要云平台提供什么样的多租户级别,这里没有唯一的答案,适合自己的才是最好的。要认真研究企业的应用系统的特征,包括应用系统的功能和它的重要程度、业务量的变化范围、安全要求等。
- 数据安全方面,某些类型的租户(比如银行、政府单位等)对数据安全非常关注,这一类用户是很难容忍将自己的数据与其他租户放在一起的,往往选择多租户等级较低的云环境,比如私有云或者专有云,或者要求一个公有云中的物理隔离的硬件等。
- 对于一般用户,选择共享程度高的云环境,往往会带来更好的经济性,比如公有云。
2. 资源共享带来的问题和租户隔离
2.1 安全性:旁侧攻击
以公有云为例来说明多租户共享带来的安全问题。公有 IaaS 云,比如 AWS EC2 和 Google GCE 等,允许其客户在共享的物理基础设上创建和运行虚机。这种资源共享能够带来规模化成本效益,但是也带来了在一个服务器上一个虚机和故意或者无意的恶意虚机共存的可能。研究表明,当两个虚机共同存在于一个物理服务器上时,存在突破虚拟化逻辑隔离而获取其它虚机/用户的机密信息或者降低其性能的可能性。最著名的也许是旁侧攻击(side-channel attacks),这种攻击可以通过聪明地监控共享资源的使用来突破虚拟化边界而窃取用户数据。
2.2 安全性:性能降低
以 SaaS 应用来说明。通过在多个客户组织(称为租户,tenant)之间共享硬件和软件资源,多租户已经取得了令人信服的降低运营成本的效果。在云计算领域中,通过划分/共用资源、减少管理和运维成本来降低运行成本。最大的成本降低效果是通过应用层面多租户(application-level multi-tenancy)来实现的,然而,这种方式会带来不同租户之间性能如何隔离(一个租户的行为不能太影响别的租户)的问题,以及如何实现不同租户不同SLA。
在 SaaS 层面,多租户是一种应用的架构形式,它使得 SaaS 提供商可以通过搭建在共享的硬件和软件架构上的一个应用来同时地服务不同租户中的用户。这么做,可以通过更好地利用基础架构资源,以及简化维护和管理来显著降低运行费用,比如,对应用做一次升级就可以使得所有用户获得最新版本。
SaaS 多租户可以在不同的层面实现:(1)利用虚拟化技术在基础架构层实现,比如给不同的租户创建单独的虚机,在虚机中运行应用 (2)在中间件层实现:共享操作系统和中间件,租户拥有单独的应用,比如容器 (3)在应用层实现:所有租户共享单个或者若干个应用,在应用内或者应用访问层实现租户隔离。其中,在应用层实现可以最大限度地提高效率:所有底层架构,包括存储、操作系统、中间件和应用都在不同的租户之间共享。
然而,在应用层实现多租户,是最难实现性能隔离的。性能隔离的目标包括:(1)阻止一个租户使用应用影响到别的租户使用应用的性能 (2)确保每个租户的可能不同的 SLA。根据租户的需求,往往是价格,每个租户的 SLA 可以不同。
举个例子,一个 SaaS 供应商提供一个在线酒店预订系统作为一个在线软件服务给他的租户,比如旅游中介。每个旅游中介的员工和客户都是该租户的一个终端用户。这种应用在季节性需求旺盛或者举行促销时,租户的请求往往达到短期的峰值。因此,对于 SaaS 供应商来说,如果确保一个需求达到峰值的租户(比如正在做促销的租户)不会影响其它租户使用该应用的性能就非常关键了。除此以外,每个租户可以要求不同的 SLA,根据他们的需要。比如,一个大型的旅游中介往往要求在峰值期间能够处理更大量的请求。
为了说明这种租户的影响,我们使用两种方式来部署应用。第一种方式,给每个租户分配一个虚机,这种情况下,应用不是多租户的。性能隔离通过虚机实现,请求大的租户也没法获得分配给他们的虚机的资源以外的更多资源。第二种方式,在物理基础设施集群上部署一个多租户的应用集群。每个虚机中运行同样应用的一个实例,所有租户都可以访问。这个集群使用一个 FIFO 的负载均衡器来转发所有租户的请求到三个虚机上。而在应用层,没有做别的事情来做性能隔离。
第一种方式中,实现了不同租户之间的性能隔离,但是,这种方式有可能造成某个应用示例出现空闲。但是,第二种方式中的应用层面的多租户,一个租户的峰值请求明显地将会影响别的租户。理想情况下,云管理系统会监控访问请求的增长,并会在它出现峰值时增加新的虚机到集群中来处理新增请求。但是,这种方式需要至少几分钟的时间来启动新的虚机。
2.3. IaaS 层面的租户隔离
一个私有云往往是单租户的,因为它部署在一个企业的数据中心内,被该企业内的员工使用。一个公有云往往是多租户的,因为从成本考虑,所有的租户都共享硬件或者软件资源,租户隔离粒度相对较高。但是在某些时候,私有云或者公有云也需要较高的隔离粒度,比如一个公司内的财务部门,出于安全性考虑,需要它所使用的虚机与别的部门使用的虚机隔离开来;比如一个政企部门,在公有云上,要求它的所有虚机或者存储必须在单独的服务器上。这种情况下,可以分为两种隔离粒度:
- 在一个云内的有限隔离粒度:虚机被物理地隔离在指定的服务器上,但是存储和网络实现逻辑隔离就够了。
- 在多个云内的完全隔离:这就是专有云,一个用户完全使用专属物理资源来搭建它的专有云。
隔离可以在不同的层面,使用不同的技术实现:
- 物理层面的隔离:这是最完全的隔离,不同的租户使用不同的物理硬件,包括计算和存储服务器和网络设备等。
- 计算在操作系统层面的隔离:虚机,一方面它提供较高的安全隔离性,另一方面,它结合其它技术比如 Linux cgroup 来实现性能保证。
- 计算在中间件层面的隔离:比如容器,它共享操作系统等底层资源,使用 Linux namespace 等技术提供一般的隔离性。
- 网络在逻辑层面的隔离:使用 VLAN, VxLAN,GRE 等方式来实现网络流量的逻辑隔离。
- 存储在应用层面的隔离:比如 Ceph 和 Swift 等共享存储,提供应用层面的租户隔离性。
存储在应用层面提供隔离的局限性:
以 OpenStack Swift 为例,它跟其它大多数的云存储系统一样,在应用层级实现租户隔离,它的各种服务都是运行在特权级别(privileged level),使得特权用户能够访问 Swift 存储或者 Proxy节点上的所有租户和用户的数据。Swift 使用可插拔式访问控制模块来加强租户隔离,比如使用 OpenStack Keystone 或者 tempAuth。 同时,Swift 支持 container ACL,这些 ACL 数据保存在某个节点上的数据库中,然后被 swift-proxy 从数据库中获取并保存在 memcached 内,来实现高速数据访问。因此,一方面,这种实现可以使 Swift 非常轻量级,能够支持大量的租户,但是,它也带来一个潜在的问题,就是一个攻击者(attacker)能够获取属于任何租户的任何用户的数据。而且,在 Swift 组件内来阻止这种攻击非常困难,因为每个节点本身会保存不同租户的数据,包括使用 memcached 来保存敏感的访问控制数据。
3. OpenStack 多租户共享和隔离(技术层面)
3.1 OpenStack 是被设计来支持多租户的
默认情况下,OpenStack 私有云和专有云是单租户模式的,因为其基础设施是由一个租户专享的;OpenStack 公有云或者社区云是多租户模式,因为其基础设施是由多个租户共享的。
3.2 多租户隔离
某些租户往往要求在计算、网络和存储等方面的物理或者逻辑隔离性。OpenStack 在这些方面分别有不同的技术来实现所需的隔离性。
3.2.1 计算/虚机隔离:租户的虚机被创建在指定的计算计算节点上
有些对安全性和性能要求高的用户,往往会要求将它的虚机部署在指定的专有的服务器上。OpenStack Nova 的这个 blueprint 在2013年引入了通过 Host aggregate 技术来支持这种需求的实现。
具体步骤包括:
(1). 增加 Nova scheduing filter
在 nova.conf 文件中,添加新的 filters:
scheduler_default_filters = AggregateMultiTenancyIsolation, ......
AggregateMultiTenancyIsolation: 该 filter 将租户待创建的虚机分配到指定的 host aggregate 上。如果一个 host 在一个 host aggregate 中,而且该 host aggregate 带有 filter_tenant_id 元数据,那么该 host 上只能创建该元数据指定的 tenant 的虚机;一个 host 可以在多个 host aggregate 中;如果一个 host 不在任何 host aggregate 中,那么所有 tenant 的虚机都可以部署在它上面。
class AggregateMultiTenancyIsolation(filters.BaseHostFilter):
"""Isolate tenants in specific aggregates.""" def host_passes(self, host_state, filter_properties):
"""If a host is in an aggregate that has the metadata key
"filter_tenant_id" it can only create instances from that tenant(s).
A host can be in different aggregates. If a host doesn't belong to an aggregate with the metadata key
"filter_tenant_id" it can create instances from all tenants.
"""
spec = filter_properties.get('request_spec', {})
props = spec.get('instance_properties', {})
tenant_id = props.get('project_id') context = filter_properties['context'].elevated()
metadata = db.aggregate_metadata_get_by_host(context, host_state.host, key="filter_tenant_id") if metadata != {}:
if tenant_id not in metadata["filter_tenant_id"]:
LOG.debug(_("%(host_state)s fails tenant id on "
"aggregate"), locals())
return False
return True
(2)创建一个租户,并给它创建一个 host aggregate
#创建tenant 和 host aggregate创建一个新的 tenant,记下它的ID:
$ keystone tenant-create --name sammytenant --description="tenant for sammy" #创建一个用户
#keystone user-create --name sammy --tenant sammytenant --pass **** --enabled true
#创建一个新的 host aggregate,记住它的 ID:
$ nova aggregate-create sammyagg
(3). 添加计算节点到该 host aggregate 中
$ nova aggregate-add-host <aggregate_name> <host_name>
比如:
$ nova aggregate-add-host sammyagg hkg02kvm004ccz023
注意这里的 host_name 是 nova host-list 命令的输出,而不是 nova hypervisor-list 命令的输出。
(4)将 host aggregate 和 tenant 联系起来
host aggregate 的 filter_tenant_id 元数据使得该 host aggregate 中的计算节点只能被指定的 tenants 使用。
#更新 host aggregate metadata,设置 host aggregate 的 filter_tenant_id 为刚才创建的 tenant 的 ID,这将使得 Nova scheduler 只会将根据提交请求的 tenant 的 ID来选择合适的 host aggregate 总的host:
root@hkg02kvm004ccz023:/etc/ceph# nova aggregate-set-metadata sammyagg filter_tenant_id=sammytenant
Metadata has been successfully updated for aggregate 2.
+----+----------+-------------------+---------------------+--------------------------------+
| Id | Name | Availability Zone | Hosts | Metadata |
+----+----------+-------------------+---------------------+--------------------------------+
| 2 | sammyagg | - | 'hkg02kvm004ccz023' | 'filter_tenant_id=sammytenant' |
+----+----------+-------------------+---------------------+--------------------------------+
(5)租户创建虚机
该虚机会被 Nova scheduler 安排部署在特定的计算节点上。
3.2.2 Cinder 物理隔离:不同租户的卷创建在不同的后端存储上
Cinder 支持租户在指定的 backend 上创建卷 volume。步骤如下:
(1)管理员配置 multiple-storage backends
修改 cinder.conf 文件,配置两个cinder LVM backend,分别使用不同的 LVM groups cinder-volumes 和 cinder-volumes2:
enabled_backends=US-lvm,UK-lvm [US-lvm]
volume_driver=cinder.volume.drivers.lvm.LVMISCSIDriver
volume_backend_name= LVM_iSCSI
volume_group=cinder-volumes
[UK-lvm]
volume_driver=cinder.volume.drivers.lvm.LVMISCSIDriver
volume_backend_name = LVM_iSCSI_2
volume_group=cinder-volumes2
(2)管理员创建 volume type
$ cinder type-create <volume type name>
$ cinder type-key <volume type name> set volume_backend_name=<backend name> 比如:
$ cinder type-create type1
$ cinder type-key type1 set volume_backend_name=LVM_iSCSI
$ cinder type-create type2
$ cinder type-key type2 set volume_backend_name=LVM_iSCSI_2
(3) 管理员设置 volume type 的 quotas。比如:
$ cinder quota-update --volumes --volume-type type1 <tenant_ID>
$ cinder quota-update --volumes 100 --volume-type type2 <tenant_ID>
(4)租户使用 volume type 创建卷,该卷会被创建在特定的 storage backend 中。
cinder create --name test --volume-type type1 1 #该 volume 会被创建在 cinder-volumes LVM Volume Group 中
3.2.3 Neutron 的逻辑隔离
OpenStack 私有云使用的网络设备往往包括 ToR 和核心交换机,路由器和防火墙等。这些设备价格昂贵,因此往往采取逻辑隔离的方式来保证安全性。Amazon Virtual Private Cloud (Amazon VPC) 也是类似的方式,它允许用户在Amazon AWS 云中预配置出一个采用逻辑隔离的部分,让您在自己定义的虚拟网络中启动 AWS 资源。
这部分的内容包括以下几种:
- Linux 网桥中 VLAN 模式租户隔离 (参考链接:Linux bridge + VLAN)
- Open vSwitch VLAN 模式租户隔离 (参考链接:Open vSwitch + VLAN)
- Open vSwitch VxLAN 模式租户隔离 (参考链接:Open vSwitch + VxLAN/GRE)
- Open vSwitch GRE 模式租户隔离 (参考链接:Open vSwitch + VxLAN/GRE)
在别的文章中都有说明,这里不再赘述。
3.2.4 Glance + Swift 多租户逻辑性隔离
(1)默认情况:单租户模式,使用单个 Swift 用户和单个 Container 保存所有用户的镜像文件
用户的虚机镜像文件通过 Glance 服务保存在 Swift 中。默认情况下,Glance 使用一个统一的 Swift 用户名和密码在一个 Swift 存储中保存所有用户的镜像文件,这种模式被称为单租户Swift (Single Tenant Swift),它使用一个Swift 账号保存所有用户的镜像在一个Swift 容器(container)中。这种方式的坏处是:
实现 | 问题 |
使用单个 Swift Container | (1)容器删除风险:如果不小心删除了该容器,那么所有的镜像文件都将丢失
(2)Swift rate-limiting: Swift 的 rate-limiting 技术使用在 container 层面,可以限制其POST/DELETE/PUT 操作的性能。如果一个 container 中镜像过多,则其可能会被限制。 |
使用单个Swift 用户 |
(1)用户删除风险:如果不小心删除了该租户,那么所有的镜像文件都将丢失 (2)密码泄露风险:如果所使用的用户的密码被泄露了,那么所有的镜像文件都面临风险 |
(2)改进1:使用 单租户模式(单Swift 用户)+ 多个 Swift Containers
Blueprint:Glance Swift Store to use Multiple Containers for Storing Images
Glance 配置参数:
swift_store_multi_tenant=False # #默认是 false,因为该支持基于单租户模式
swift_store_multiple_containers_seed = < 到 之间的整数>
#默认为 ,表示使用单租户模式来保存所有用户的镜像文件。当设置为 到 之间的整数时,将会使用单租户 Swift 但是多个containers 来保存镜像,而 container 名称则取决于镜像的 UUID 字符串中的指定长度的子串。比如设置其值为 ,而且 swift_store_container = ‘glance’,那么UUID 为 ‘fdae39a1-bac5--aba4-69bcc726e848’ 的镜像将被保存在 ‘glance_fda’ container 中。可见,这种模式中,所有用户的镜像将会被分散地保存到多个 container 中。
(3)改进2:使用多个 Swift 用户,使能多租户模式
Blueprint:Store image data in tenant-specific swift accounts 每个租户的镜像保存在其自己的 Swift account 中。
Glance 配置参数:
- swift_store_multi_tenant = true #设置为 true,使能多租户模式,使得每个租户的镜像被保存在该租户对应的 swift 账号中
- swift_store_admin_tenants = <<tenant_id>:<username> <tenant_name>:<username> *:<username>> # openstack租户:swift账号
手头没有环境,因此没有一步一步做实验。详细配置步骤和实现细节可以参考 Configure tenant-specific image locations with Object Storage 和 https://review.openstack.org/#/q/topic:bp/swift-tenant-specific-storage,n,z。
3.2.5 Ceph 的隔离性
(1)使用 pool 做逻辑隔离
在使用默认的 CRUSH rules 的情况下,一个 Pool 所使用的 OSD 可能分布在不同的存储节点上。这时候,租户 - pool - osd 其实是逻辑隔离关系。
(2)定制 CRUSH Rules 对 Pool 做物理隔离
Ceph CRUSH Rules 会对数据如何存放在 OSD 上有直接的影响。因此,定义合适的 Rules,可以使得一个 Pool 的 OSD 分布在指定的存储节点上。
3.3 单个 OpenStack 云中
3.3.1 默认的多租户共享和逻辑隔离
3.3.2 按需配置的特定租户物理和逻辑隔离
4. OpenStack 多租户共享和隔离(运行层面)
多租户环境中,除了IaaS 技术层面的隔离以外,还有就是在运行层面的隔离,比如分租户计费、分租户的 Dashborad、运维、以及“店中店” 模式(租户 - 子租户模式)等等。而这些目前OpenStack 中并没有完善的解决方案。
参考链接:
- https://bugs.launchpad.net/glance/+bug/1155389
- https://blueprints.launchpad.net/glance/+spec/swift-store-multiple-containers
- https://www.openstack.org/summit/vancouver-2015/summit-videos/presentation/multi-tenant-isolation-quarantine-your-tenants
- https://developer.salesforce.com/page/Multi_Tenant_Architecture
- http://www.ibm.com/developerworks/cloud/library/cl-multitenantcloud/
- https://www.mirantis.com/blog/baremetal-provisioning-multi-tenancy-placement-control-isolation/
- https://github.com/wbentley15/os-summit-multitenant-wk/blob/master/multi_tenant_iso_lab.txt
- https://wiki.openstack.org/wiki/Cinder-multi-backend
https://blueprints.launchpad.net/glance/+spec/support-multiple-swift-backends
- https://region-a.geo-1.objects.hpcloudsvc.com/v1/61624292678963/public_referenced/logical-swift-store.pdf
OpenStack 企业私有云的若干需求(3):多租户和租户间隔离(multi-tenancy and isolation)的更多相关文章
- OpenStack 企业私有云的若干需求(7):电信行业解决方案 NFV
自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云(Hybrid cloud)支持 主流硬件支持.云快速交付 ...
- OpenStack 企业私有云的若干需求(4):混合云支持 (Hybrid Cloud Support)
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(2):自动扩展(Auto-scaling) 支持
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(5):主流硬件支持、云快速交付 和 SLA 保证
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(1):Nova 虚机支持 GPU
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(6):大规模扩展性支持
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(10):OpenStack 的前景和钱景
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack 企业私有云的若干需求(9): 云管理平台 CMP
本系列会介绍OpenStack 企业私有云的几个需求: 自动扩展(Auto-scaling)支持 多租户和租户隔离 (multi-tenancy and tenancy isolation) 混合云( ...
- OpenStack企业私有云新需求(1):Nova 虚机支持 GPU
作者:Sammy Liu 刘世民 本系列会介绍OpenStack 企业私有云的几个需求: GPU 支持 自动扩展(Auto-scaling)支持 混合云(Hybrid cloud)支持 物理机(Bar ...
随机推荐
- 背水一战 Windows 10 (16) - 动画: ThemeAnimation(主题动画)
[源码下载] 背水一战 Windows 10 (16) - 动画: ThemeAnimation(主题动画) 作者:webabcd 介绍背水一战 Windows 10 之 动画 PopInThemeA ...
- EC笔记,第二部分:6.若不想使用编译器默认生成的函数,就该明确拒绝
6.若不想使用编译器默认生成的函数,就该明确拒绝 1.有的时候不希望对象被复制和赋值,那么就把复制构造函数与赋值运算符放在private:中,但是这两个函数是否需要实现呢?假设实现了,那么你的类成员方 ...
- C++_系列自学课程_第_6_课_bitset集_《C++ Primer 第四版》
在C语言中要对一个整数的某一个位进行操作需要用到很多的技巧.这种情况在C++里面通过标准库提供的一个抽象数据类型 bitset得到了改善. 一.标准库bitset类型 1.bitset的作用 bits ...
- 常见.NET功能代码汇总 (2)
常见.NET功能代码汇总 23,获取和设置分级缓存 获取缓存:首先从本地缓存获取,如果没有,再去读取分布式缓存写缓存:同时写本地缓存和分布式缓存 private static T GetGradeCa ...
- 【转】微信小程序给程序员带来的可能是一个赚钱的机遇
自上周被微信小程序刷屏之后,这周大家都在谈微信小程序能够带来哪些红利的话题,其实我想从程序员的角度来谈谈,带给我们程序员来的红利,或许是我们程序员创业或者赚钱的机遇. 其实我从<作为移动开发程序 ...
- JS高程3.基本概念(5)语句
1.if语句 2.do-while语句:后测循环语句,循环体内的代码至少执行一次. 3.while语句:前测循环语句. 4.for语句:前测循环语句. 注意:在ECMAScript中不存在块级作用域, ...
- JavaScript-数组去重由慢到快由繁到简
indexOf去重 Array.prototype.unique1 = function() { var arr = []; for (var i = 0; i < this.length; i ...
- Android—自定义控件实现ListView下拉刷新
这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家): 首先下拉未松手时候手机显示这样的界面: 下面的 ...
- ImageLoader配合ImageSwitcher的使用
先在MyApplication中初始化ImageLoader initImageLoader(getApplicationContext()); /** * 初始化ImageLoader * 如果你经 ...
- Material Design 概念,环境和基本属性
Material Design 概念,环境和基本属性 Material Design是随Android 5.0推出的一种设计概念, 涉及到了跨平台和设备的视觉,动态,交互设计等方面. 设计概念 M ...