相比于瞬息万变的互联网应用,企业级应用如何做到快速的兑现客户承诺,及时的响应用户和市场需求?技术转型是必然趋势,不破不立,微服务理念的崛起恰好给了企业技术转型的绝好机会。但微服务的改造之路并不是一帆风顺的,这里不仅涉及到技术层面的改造,还有管理模式,工作方式的改变。本次分享浅谈一些个人看法和Mico Focus公司的实践经验。

正文开始之前,让我先简单的介绍一下我们的产品,Service Management Automation X(SMAX)来自于全球领先的纯软件公司Micro Focus,基于ITIL(Information Technology Infrastructure Library)实现,ITIL是全球公认的一系列IT服务管理最佳实践。ITIL的基本架构可参见下图:

困局?

2017年的时候,我们整个项目组还在使用瀑布模型的方式开发、发布产品。每次产品的迭代发布周期是一年。相比于瞬息万变的互联网应用,可能每个月都有一次版本更新,我们的产品版本更新太慢。为何要如此频繁的更新产品呢?频繁的发布更新可以快速的兑现客户承诺,及时的响应用户和市场需求,最大化的实现商业价值,小步快跑的方式抢占市场。对于企业级应用来说,也是一样的,我们需要快速的兑现商业价值,让客户尽早的用上新功能。因此从公司的战略层面,决定将版本更新的频率提升4倍,发布周期从原来的1年,缩短为3个月。

要实现这个战略目标,对现有的组织架构、技术架构等都是非常大的挑战。我们需要一种新型战术来满足我们的业务需求。微服务正好可以解决产品快速迭代的问题,并且具有以下优点:

  • 提升开发交流,每个服务足够内聚,足够小,代码容易理解;

  • 服务独立测试、部署、升级、发布;

  • 容易扩大开发团队,可以针对每个服务组建开发团队;

  • 提高容错性,一个服务的内存泄露不会导致整个系统瘫痪。

最终我们选择了微服务,帮助我们快速的迭代产品,快速的实现商业价值。同时在开发流程上我们开始使用敏捷开发,并倡导DevOps文化,希望各个小团队可以达到团队自治,对市场做到快速响应。

破局?

当确定了战略目标后,就是怎么做了?可以从两个角度来讨论。

  • 管理层面

  • 技术层面

管理层面

微服务的转型之路,这个决定必然是由上而下的,从管理层到具体的每个开发人员,我们必须在观念上达成一致,不破不立,敢于尝试新技术,但也不可盲目采用新技术,为了技术而技术,前期适当的小规模验证过程是很有必要的。对于一些腐朽的旧思想也要能做到果断的抛弃。

我们整个开发团队的人数达到了数百人,对于如何管理一个数百人的敏捷团队,也缺乏业界的最佳实践,我们也只能摸着石头过河。对于如何做好敏捷开发,我们依然在不断探索中,找到最适合我们的方式。对于大型企业,现在业界有一套完整的敏捷开发解决方案SAFe,我们也在尝试中。

技术层面

回到技术层面,我将从以下几个方面来讲我们的转型之路:

  • 技术选型

  • 服务拆分

  • 安全

    • 容器安全

    • 敏感信息保护

  • 企业级可用(Enterprise Ready)

    • 健康检查

    • 零停机升级

  • DevOps

    • Pipeline

    • 性能测试

    • 监控

    • GitOps

技术选型

现在一提到微服务,很多人会想到容器技术,比如Docker。

那么微服务和容器之间到底有什么关系呢?我的回答是,微服务和容器没有任何关系。

微服务理念出现的时间要比容器技术早很多,其理念在70年代被提出。而容器技术在2013年才被提出,它最初由一个叫做dotCloud的项目发展而来,后来改名叫做Docker。基于微服务的思想开发应用程序是完全可以不用容器技术的,例如现在流行的Spring Cloud和Dubbo都是不使用容器技术来实现微服务思想的。

那为什么微服务和容器技术总是被同时提起?这主要是出于以下两个原因:

  • 按照微服务的理念,如果使用容器作为基础设施,能够实现快速部署,快速迭代,独立运行;

  • 在云计算中,容器作为替代虚拟机的基础设施受到大家的关注度更高。

如果能将容器技术应用于微服务中,简直能达到事半功倍的效果。既然是微服务,必然会产生许许多多小的服务,如何对其进行管理就成为了我们很现实的问题。2017年时,容器编排平台的百花齐放,我们犹豫了,到底该怎么选择?当时很多互联网公司开始自研自己的容器编排平台,虽然这能获得极大的独立自主性,但同时自研也需要大量的研发投入,这是我们不能承受的。然后我们把目光放到了开源社区,当时比较知名的容器编排平台有Kubernetes和Docker Swarm。

可能出乎很多人的意料,我们一开始用的是Docker Swarm,选它的理由也很简单,Docker Swarm上手快,学习曲线平缓。但在使用或者尝试的过程中,我们低估了我们产品的复杂性,Docker Swarm远不能满足我们的需求。最终,我们又决定转移到Kubernetes平台,万幸的是,我们并没有在Docker Swarm上花太多时间,平台转移的决定也做的很坚定。这在微服务改造的过程中是很关键的,人总会有犯错的时候,勇于承认错误,并不断地做调整,不拖泥带水优柔寡断。一定的试错成本是不可避免地,不要害怕试错。

现在回过头来看,我们当时选择Kubernetes的决定,真的是“赌”对了。Kubernetes自身就是一个完整的分布式系统解决方案,支持服务发现,服务注册,扩缩容,跨主机部署,企业级就绪,自我修复,自动重启等功能,能帮助我们省下一大笔自研的投入。

Kubernetes起源于Google内部系统Borg,经过多年的实际运行,更成熟,更稳定。更是依托于活跃的社区,以及CNCF基金会的加持,云原生技术的不断发展,我们可以很容易的从社区得到帮助,以及很多非常棒的技术实现,帮助我们解决了很多问题。比如Hashicorp公司的Vault,一款出色的存储敏感信息的软件,还有监控软件Prometheus。

服务拆分

有了技术选型,Kubernetes和Docker,我们就可以进一步进行微服务拆分。服务拆分,如何拆分?拆到什么程度?

谈到服务拆分,不得不提的就是著名的“康威定律”,“软件架构会反映出公司的组织架构,组织架构又反过来影响着软件架构。”

为获得更大的沟通效益,微服务架构的改造必然会导致组织架构的改变。我们需要在现有技术架构,组织架构间不断做调整,平衡两者的关系,最终将服务拆分为四大模块。

  • 事件管理、问题管理、变更管理、服务资产/配置管理

  • 租户管理

  • 智能分析

  • 通用服务

每个业务模块有一个团队负责开发维护,每个团队的人数大概是3-5人,并由团队自己做适当的继续拆分,落地为真正提供服务的Pod。

个人认为,对于大型企业级应用来说,因为本身架构、技术债务、组织架构等等原因,不急于将应用程序拆的非常细。一是拆的越细成本越高,花费的时间也会越长,短期内看不到业务价值,就很难说服业务方同意此方案。二是从未来来看,不能保证现在的拆分就是合理的,应遵循小步快走,快速试错的原则。

每个组件必须可以独立运行,这是业界对于微服务的公认标准。这里对于组件的定义,个人认为可以扩展到由多个Pod组成,共同提供功能,而不是要求每个Pod必须可以独立的提供功能。对于现有应用程序的微服务改造,不考虑现状,过高的标准是不合时宜的。我们可以以架构演进的方式,慢慢迭代开发,最终不断完善我们的应用程序。

总结来说,我们的拆分原则是,先按业务功能分成大的模块,在由各个团队,结合业务和技术实现继续拆分。同时针对微服务中的一些通用功能可以成立一个独立团队负责,常见的通用功能有路由模块、鉴权授权模块。

安全

容器安全

在以色列,我们有专门的安全团队负责对我们的产品进行安全扫描,可以从以下几个维度来检查及保证容器的安全性。

恶意及脆弱镜像,在Docker Hub市场中有成千上万的免费镜像,可随时在Docker容器中使用。然而一项研究表明,在测试的2500多个Docker镜像中发现了大量安全漏洞。选择官方或可信的镜像,避免引入脆弱组件,更甚者是恶意代码。Docker Hub也提供付费计划,可以对镜像进行“安全扫描”,检查镜像中的已知漏洞。

过量资源使用,一般情况下,Docker容器没有资源限制。因此,不受限制的Docker容器可能导致宿主机性能严重下降。要对内存、带宽和磁盘使用(I/O)设置资源限制,保证整体性能的稳定。

容器突破,在 Dockerfile 中,如果我们不显式指明用户,进行权限处理,那么Docker容器默认在运行时会以root身份运行,以root身份启动 Docker容器是一件很危险的事情。尽管,Docker容器内的root与宿主host本身的root并不一定具有一样的权限,但是在容器内部的root拥有和宿主机一样的UID(UID 0)。如果以priviledge的方式运行container,那么两者将会一样,从而产生巨大的安全隐患。应遵守最小权限原则,避免潜在的安全风险。

对Docker镜像进行签名及验证,保证客户能拿到真正由我们公司发布的Docker镜像,防止黑客对Docker镜像的恶意篡改。

不要在Docker镜像中存放敏感信息,现在市面上检查容器安全的工具有很多,我们目前使用的工具有Anchore(https://anchore.com/)和Aujas(https://www.aujas.com/)。

敏感信息保护

微服务间各个Pod之间的通信需要进行安全认证,需要用到证书以及用户名密码。证书该如何被安全的分发到各个Pod内部呢?Pod应该如何获取密码?基于Kubernetes强大的生态系统,Hashicorp公司的Vault正好可以满足我们的技术需求。有关Vault的更多使用可以访问官网:https://www.hashicorp.com/products/vault

企业级可用(Enterprise Ready)

健康检查

作为企业级软件,我们的很多客户都是跨国公司,在各个国家都有员工,跨时区,则要求我们的应用程序必须24小时稳定提供服务。那该如何保证高可用?

在软件行业,你可能经常听见这样一句话,“要不你重启试试?”的确,很多问题都可以通过重启解决,借助于Kubernetes提供的自愈功能,我们为所有的Pod设置了合理的Liveness探针和Readiness探针来提高整体应用的可用性。

Kubernetes使用Liveness Probe(存活探针)来确定何时重启容器。例如,Java程序内存泄漏了,程序无法正常工作,但是JVM进程却是一直运行的,对于这种应用本身业务出现了问题的情况,通过检测容器响应是否正常来决定是否重启,这是一种很好的健康检查机制。

Kubernetes使用Readiness Probe(就绪探针)来确定容器是否已经就绪可以接受流量。只有当Pod中的容器都处于就绪状态时Kubernetes才会认定该Pod处于就绪状态。该信号的作用是控制哪些Pod应该作为Service的后端。如果Pod处于非就绪状态,那么它们将会从Service的Endpoint中移除。

配置有效的Liveness Probe

Liveness Probe应该检查什么?

一个好的Liveness Probe应该检查应用内部所有关键部分是否健康,并使用一个专有的URL访问,例如/health,当访问/health时执行这个功能,然后返回对应结果。这里要注意不能做鉴权,不然Probe就会一直失败导致陷入重启的死循环。

另外检查只能限制在应用内部,不能检查依赖外部的部分,例如当前web server不能连接数据库时,这个就不能看成web server不健康。

Liveness Probe必须轻量。

Liveness Probe不能占用过多的资源,且不能占用过长的时间,否则所有资源都在做健康检查,这就没有意义了。例如Java应用,就最好用HTTP GET方式,如果用Exec方式,JVM启动就占用了非常多的资源。

零停机升级

产品总是在不断迭代中的,SMAX也不会只有一个版本。在Kubernetes集群中,如何升级应用程序,并且最小的宕机时间?

这里不得不佩服Kubernetes的强大,已经为我们提供了现成的解决方案,Rolling Update(滚动升级)。Kubernetes的Rolling Update提供了两个参数maxSurge和maxUnavailable来控制滚动升级的速度,不同的速度会导致不同的现象。

创建一个额外的Pod(新),然后删除一个旧的Pod(maxUnavailable = 0,maxSurge = 1)

假设我们有3个Pod,此配置允许在原有Pod的数量之上添加一个额外的Pod(maxSurge = 1),而可用Pod的数量不能低于3(maxUnavailable = 0)。此种配置保证了在新的Pod还没有正常提供服务时,依然会有旧的Pod存在,旧的Pod依然可用对外提供服务。但这种配置,在保证宕机时间的同时,也对应用程序提出了新的挑战,新旧Pod必须保证前后兼容。还有一个缺点就是更新Pod慢。

删除一个Pod,然后添加一个新的Pod(maxUnavailable = 1,maxSurge = 0)

此配置不允许创建额外的Pod(maxSurge = 0),但允许有一个Pod不可用(maxUnavailable = 1)。在这种配置下,Kubernetes将首先停止一个Pod,然后再创建一个新的Pod。这种配置的主要好处是,不需要额外的计算资源,毕竟多创建一个Pod就是多一份计算资源消耗。

尽可能快地更新Pods(maxUnavailable = 1,maxSurge = 1)

此配置允许在额外创建一个Pod(maxSurge = 1)的同时,允许有一个Pod不可用(maxUnavailable = 1)。第三种配置结合了以上两种配置的优缺点,极大地减少了更新Pod的时间。

这三种配置方式在我们SMAX产品中都存在,关键是结合自身选择最适合自己的配置方式。

以上就是我们产品SMAX的微服务迁移之路,想必每家公司的迁移之路都是不同的,全盘照搬是行不通的,要结合自身的行业属性,公司组织架构,人员配置,现有技术架构,不断地做权衡,设置优先级。完美的技术架构不可能是一步到位,需要不断地演进迭代。

伴随着微服务的改造,部署集群、测试等变得异常复杂,因此需要一种新的技术理念,提高工作效率,DevOps应运而生。DevOps是一种方法,它使开发人员和运维人员能够更紧密地协作,从而更快地交付高质量的软件。

DevOps

Pipeline

对于构建一个微服务集群来说,这是一个庞大而复杂的集成与交付步骤,如果这些步骤依然停留在手工操作的阶段,将严重拖慢“快速交付”的理念。通过Jenkins pipeline实现持续集成和持续交付(CI/CD)管道,完成自动化构建、测试和部署应用程序。

在开发和运维人员之间架起一座桥梁,方便开发人员快速验证新代码功能,保证质量。

在Pipeline方面,我们自研了很多的工具包,比如在2017年的时候,还没有kubeadm,我们自己开发了一套程序,可以做到快速的创建Kubernetes集群,并将整个SMAX集群部署其上,一切只需一条指令,大大节约了开发测试人员部署环境的时间。

性能测试

目前我们使用LoadRunner对整个集群进行压力测试,当前我们大约有50个关键事务(Transactions)用于压力测试,这50个关键事务基本涵盖了SMAX的核心功能。并且我们还有150个非关键事务处于开发调试阶段,力求对SMAX进行更完整全面的压力测试。未来我们依然会不断扩充我们的事务数量用于压力测试。

我们将上一次发布版本的测试结果作为基准线,计算此次压力测试的结果,性能是否有提升,还是衰退。通常如果有10%左右的性能衰退,我们就会引起警觉,花时间花人力进一步分析原因。参考LoadRunner的测试报告和其他的监控报告,帮助我们更快的定位问题。

监控

当前我们从虚拟机和Kubernetes的两个角度来监控整个集群。虚拟机监控可以给到我们一个整体的性能报告。Kubernetes集群监控可以更具体到各个Pod的性能报告。

虚拟机监控,我们使用了Zabbix监控整个集群的硬件使用率,比如CPU,Memory,磁盘IO等。这些参数相对来说还是比较粗略的查看集群的整体状态,但可以给到我们一个比较直观的整体性能趋势报告。当某些性能指标出现异常(超平均的5% - 10%)时,我们能快速的发现问题,然后采取措施,进一步分析问题。是否是新代码的引入导致了性能问题?

Kubernetes集群监控,利用Prometheus+Grafana搭建监控系统,我们可以很直观的看到Pod的运行状况,何时Pod的CPU和Memory处于高负载阶段,高负载的持续时间是否和LoadRunner压力测试的时间片吻合,如果不吻合,我们需要进一步分析导致高负载的原因。如果高负载的百分比大于平均值,也同样需要引起警觉,可能是一个性能下降的信号。

GitOps

GitOps要求在版本控制软件中(如GitHub,GitLab和Bitbucket),使用声明性规范(比如yaml文件)存储系统所需的状态,这样对整个系统的改动,都是可审计跟踪的,每一个改动都包含了提交时间、提交者,这意味着基础设施可以和应用程序一样做到版本化。

在Kubernetes中,所有的Kubernetes资源都是以声明性规范(yaml文件)的方式来创建的。对声明性规范的修改,Kubernetes会负责将集群的最终状态保持和声明性规范一致。

GitOps正是利用了Git的版本化和Kubernetes的声明性规范来描述、创建和观察整个系统,开发和交付基于Kubernetes的基础设施和应用程序的模型规范。

如果从CI/CD的角度来理解GitOps,那么每次的Pull Request被合并到主分支就是CI,CD意味着Kubernetes用Pull Request的文件改动来更新自己的Kubernetes集群。

我们运用GitOps的理念,为DevOps团队提供基础设施,如Jenkins instance,PostgreSQL数据库、Prometheus和Grafana等。

在DevOps方面,我们还有很多需要做的,比如,自动伸缩,流量监控,日志链路追踪。我们已经在开始尝试一些POC(Proof of Concept,验证性测试),相信不久的将来,我们的所有尝试都可以给客户带来真正的商业价值。

总结

架构总是处在不断演化的过程中,我们不会停止对新技术的探索,用更好的技术服务未来更复杂的业务需求,快速实现商业价值。

Q&A

Q:团队敏捷,最重要的是什么?团队最成功是那一part?

A:我认为团队敏捷最重要的应该是团队文化,要真正的是一个团队,大家万众一心,不要搞小集体,大家目标统一了就更容易办成一件事情。举个例子,在我们团队中是如何培养团队文化的?每个人团队中总会有1-2个乐于分享,专研技术的人,我们可以定期搞一些内部技术分享,小型的,时间也不用太长。慢慢的培养大家的极客精神,由1-2个人带动整个团队。以上是对某个具体团队,我认为最重要的事情。如果上升到整个产品部门或整个公司,我认为管理层的百分比支持也没关键,要有一条路走到黑的魄力。我们团队最成功的部分,我认为也就是我们团队的文化,乐于分享,我也希望这个文化可以一直保持下去。

Q:业务升级时,Kubernetes通过滚动升级策略,保证了最大在线Pod数。那请问,单个Pod如何保证流量的平滑,比如在postStart阶段会做哪些检查?

A:这个要看你Pod的具体业务是否是有状态,比如session信息等。新老Pod是否可以共存,同时提供服务。Pod提供的是在线服务还是离线服务。然后在决定在Pod生命周期的哪些阶段做哪些事情。如果我们按最简单的说:

  • 新老Pod可以共存,并且Pod提供的是离线服务。那么在preStop阶段可以先把老Pod的readiness置 为false,这样新的请求就到新的Pod上去了,然后等老Pod做完已有的离线服务就Terminate。

  • 如果说新老Pod可以共存,但不能同时提供服务,可能需要考虑蓝绿部署,通过Service的label selector来切换流量。

  • 如果在复杂的情况,我需要更多的信息,具体分析。相对应的,代码需要做的事情也会更多。我们可以群内详聊。

Q:服务拆分过程中需要注意的点有哪些?微服务架构的配置问题?旧服务集群化需要注意的点有哪些?新旧服务之间的网络是否可以互通,如果不互通如何解决?

A:个人认为服务拆分的注意点可以从“管理”和“技术”来说:

  • 管理层面,服务拆分后和现有组织架构的冲突问题如何解决,这里管理层必须要有打破旧有格局的决心,但适时的妥协不可避免。

  • 技术层面,还是根据业务来拆分,同一业务或相似业务尽量拆分在同一团队,这样业务知识至少不用跨团队。先做一个大的拆分,不是那么细的拆分,尝试一下,然后在根据业务,由团队决定继续拆分,可能会有一个大的团队变成两个小的团队。

在Kubernetes中可以使用ConfigMap和Secrets做配置,但如果是复杂应用,也可以使用开源的一些配置中心。旧服务集群化的注意点,这个问题有点大,基本今天分享的所有内容都是注意点,到时会整理成文章发布,可以关注一下。Kubernetes是可以和外部系统互通的,比如使用externalService。如果不互通,是否可以尝试使用Proxy等技术使其互通。

Q:使用Kubernetes来管理微服务会带来一些好处当然也会带来一些复杂度,比如容器平台本身维护的复杂度,你们是从哪些方面来权衡最终使用Kubernetes来管理微服务?

A:一个是自身业务的复杂度,如果业务简单,完全没必要使用Kubernetes。如果自身业务复杂,必然需要一个功能全面的容器编排平台,比如Kubernetes。甚至一些更复杂的业务,都会基于Kubernetes做二次开发。二是CNCF,云原生技术的推广,这些技术都是围绕Kubernetes的,社区的帮助也很重要,减少了自研的投入。

Q:你们的基础设施是自己公司的私有云还是用的公有云?在基础设施选择上你们会用公有云去简化基础设施的投入,而把注意力都放在软件研发上吗?

A:我们有线下机房,同时也支持部署在云端,比如Amzon EKS,Auzre AKS,GCP,阿里云。最近在尝试部署到OpenShift。其实我们是企业级应用,属于卖软件产品,最终部署在哪里?on-premise,私有云还是公有云是由客户自己决定的。所以每个平台我们都需要支持。但我们也有自己的SaaS服务是部署在AWS上,简化了基础设施的投入和维护成本。

Kubernetes管理员认证(CKA)培训

本次CKA培训将于11月20到22日在北京开课,培训基于最新考纲,通过线下授课、考题解读、模拟演练等方式,帮助学员快速掌握Kubernetes的理论知识和专业技能,并针对考试做特别强化训练,让学员能从容面对CKA认证考试,使学员既能掌握Kubernetes相关知识,又能通过CKA认证考试,学员可多次参加培训,直到通过认证。点击下方图片或者阅读原文链接查看详情。

企业级应用Service Management Automation X(SMAX)的微服务之路相关推荐

  1. 直播 | 企业级应用Service Management Automation X(SMAX)的微服务之路

    分享时间:10月27日 20:30 分享主题:企业级应用Service Management Automation X(SMAX)的微服务之路 分享人介绍:钟涛,Mico Focus高级软件工程师,S ...

  2. JavaEE 企业级分布式高级架构师(十三)微服务框架 SpringCloud (H 版)(1)

    Spring Cloud学习笔记 Spring Cloud入门 分布式技术图谱 Spring Cloud简介 官网介绍 百度百科 总结 Spring Cloud的国内使用情况 Spring Cloud ...

  3. 如果不懂Service mesh,就不要谈微服务了

    提到微服务,spring cloud等经典框架被使用的最为广泛,但是在2016年才被提起的Service Mesh,已经被Paypal.Lyft.Ticketmaster和Credit Karma等等 ...

  4. 微服务架构之「 下一代微服务 Service Mesh 」

    点击上方"朱小厮的博客",选择"设为星标" 做积极的人,而不是积极废人 Service Mesh 被大家称为下一代的微服务,是微服务领域的一颗新星,被大家讨论的 ...

  5. 微服务架构工作笔记001---认识Service Mesh

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 Service Mesh作为下一代微服务技术的代名词,初出茅庐却深得人心一鸣惊人,大有一统微服务时 ...

  6. 微服务框架之微软Service Fabric

    常见的微服务架构用到的软件&组件: docker(成熟应用) spring boot % spring cloud(技术趋势) Service Fabric(属于后起之秀 背后是微软云的驱动) ...

  7. 胡忠想|微博微服务架构的Service Mesh实践之路

    前言 说到Service Mesh,在如今的微服务领域可谓是无人不知.无人不晓,被很多人定义为下一代的微服务架构. Service Mesh在诞生不到两年的时间里取得令人瞩目的发展,在国内外都涌现出一 ...

  8. GO微服务实战第五节 为什么说 Service Meh 是下一代微服务架构?

    在前面第 2 课时我们介绍过,Service Mesh(服务网格) 是云原生的代表技术之一,并且在后面的组件案例实践中,Service Mesh 也是其中的"主角",因此我们非常有 ...

  9. 微服务时代--service meshAGW

    转载自:https://zhuanlan.zhihu.com/p/61901608 Service Mesh Service Mesh作为下一代微服务技术的代名词,初出茅庐却深得人心一鸣惊人,大有一统 ...

最新文章

  1. android 禁用组件,android
  2. 向量空间模型VSM—特征抽取算法—TF-IDF
  3. sql多表查询之一:Where 和 On的秘密
  4. html统计表合并单元格的快捷键,word怎样设置合并单元格快捷键
  5. html页面缓存meta,html中怎么用meta语句禁用页面缓存?
  6. 扫地机器人单扫和双扫_618买扫地机器人前必看 别图便宜 小心入坑!
  7. 禁用linux的密码策略
  8. tcpdump + mk-query-digest 分析mysql
  9. 使用FileWriter和BufferedWriter向文本文件中写信息
  10. 可视化导论 - 第三章 数据- 学习笔记
  11. mysql归档模式_数据库归档模式设置步骤
  12. 骨传导蓝牙耳机哪个好,五款热门骨传导蓝牙耳机推荐
  13. 你的Idea还可用吗?不妨试试另一个开发神器!
  14. (超、深)超像素网络SSN
  15. 西北工业大学-技术经济学-MOOC课程截图笔记
  16. PHP实现停车场管理系统
  17. 中国电视覆盖及收视状况调查结果出炉
  18. 关于文件的扩展名和区别源文件、目标程序文件、可执行程序文件
  19. 软件测试计划--例子
  20. 什么是EDI许可证办理需要哪些条件

热门文章

  1. 单元库质量验证方法之compare library
  2. Xilinx Ultrascale+ 使用PRC向ICAP E3核输入码流重构时无反应
  3. 老毛桃重启计算机没反应,遇到电脑无法启动时 老毛桃Win10如何原因分析解决
  4. Python游戏开发-02-生成日志写入文件
  5. win10应用商店打不开,错误代码0x80131500
  6. RHEL下编译wireshark源码
  7. StoryBoard 创建tabBarController
  8. 中医济世促醒汤-----发作性睡病
  9. 随机森林的特征重要性排序
  10. 微信特殊字符php,php 获取微信昵称时 过滤特殊字符