公司最近打算进军东南亚,在泰国、马来西亚等地方开拓开拓市场,然后就有很多国外的常用网站打不开,对业务调研来说是很大的影响。于是在网上找各种解决办法,试过一些免费梯子不怎么稳定,免费无好货,因此起了自己搭梯子的打算。

安排程序折腾了两天,查shadowsock攻略,然后用AWS搭建成功,做个笔记。

亚马逊的AWS有免费试用1年,新用户绑卡然后选择特定配置即可,大家可以动手试起来。

下面步骤主要是基于亚马逊的AWS来搭建梯子

一、创建AWS一个实例

首先注册一个亚马逊账户,获取免费一年AWS试用的资格,然后创建实例(启动实例),步骤如下:

我选择Ubuntu Server 16.04版本

设置 实例类型 储存 标签 等,默认即可

配置安全组,这步比较关键,需要对这实例增加端口配置(也就是防火墙规则),点击添加规则,例如:

类型:自定义TCP规则,协议:默认TCP,端口:8989,来源:任何位置

最后点击审核和启动

选择密钥对,下载pem文件并保存好,后续SSH登录时需要用

经过上述步骤,AWS实例是创建完成了

二、安装shadowsocks服务器版

安装python-pip

> sudo apt update

> sudo apt install python-pip

然后用pip安装shadowsocks

> sudo pip install shadowsocks

命令行启动shadowsocks,-p指端口,-k指shadowsocks登录时的密码,-m指加密方式

> sudo ssserver -p 8989 -k password -m aes-256-cfb -d start

如果要关闭,则将start改为stop即可

另外,我们还可以检查安全组配置是否正常。可以查看一下端口是否打开

>netstat -tln

三、下载shadowsocks客户端(Windows、iOS、安卓、mac分别有对应的客户端,自行百度)

这里示例Windows说一下,下载一个windows版本Shadowsocks工具

填上你AWS的IP,端口以及密码,加密方式默认是选择aes-256-cfb,代理端口1080也是默认

右键点击shadowsocks客户端图标,选择启动系统代理

Bingo!

这时应该就可以通过搭的梯子来登查资料、办公等了!

四、自行搭建的问题

自行搭建AWS服务器,需要一定的技术功底,搭建服务器是关键。另外免费试用的服务器性能一般,网速看你选的IP吧,总之还是有一定的局限性。

如果觉得自己搭建AWS有困难,可以试试现成的梯子,可以看看我博客的帖子

> 本文由博客一文多发平台 OpenWrite 发布!

01、原理

剔除数组中无用的原数,只记录有效的数据

02、代码实现

03、运行结果

04、总结

稀疏数组的第一行记录该数组的行、列、一共有多少值,从第二行开始往后,开始记录数组的坐标和值

个人感觉,该数组适用于稀疏类型的二维数组。进行存储。如果是属于那种满格的二维数组。该算法是否有效?如果说。他只是在 一定的量之前适用于该数组,那么这个值是什么?怎么去衡量他

1. 文件系统层次化标准(FHS )

Linux系统中常见的目录名称以及相应内容

目录名称

应放置文件的内容

/boot

开机所需文件—内核、开机菜单以及所需配置文件等 驱动引导文件

/dev

以文件形式存放任何设备与接口 设备文件

/etc

配置文件

/home

用户主(家)目录

/bin

存放单用户模式下可以操作的命令(普通用户可以执行的命令)

/lib

开机时用到的函数库(千万不要动)

/sbin

开机过程中需要的命令(超级用户执行的命令)

/media

用于挂载设备文件的目录(或/mnt)

/opt

放置第三方的软件

/root

系统管理员的家目录

/srv

一些网络服务的数据文件目录

/tmp

任何人均可使用的“共享”临时目录

/proc

虚拟文件系统,例如系统内核、进程、外部设备及网络状态等

/usr/local

用户自行安装的软件

/usr/sbin

Linux系统开机时不会使用到的软件/命令/脚本

/usr/share

帮助与说明文件,也可放置共享文件

/var

主要存放经常变化的文件,如日志

/lost+found

当文件系统发生错误时,将一些丢失的文件片段存放在这里

2.绝对路径和相对路径

绝对路径:就是从根目录(/)开始的路径。

例如:

相对路径:相对于当前路径的写法。

例如:

3.物理设备命名规则

scsi/sata/u盘 /dev/sd[a-p]

光驱 /dev/cdrom

硬盘的分区编码:

主分区或者扩展分区的编号从1开始到4结束。

逻辑分区从5开始。

4.文件系统

常见的文件系统:

ext3(rhel5)、ext4(rhel6)、xfs(rhel7)

注:xfs文件系统可支持高达18EB的存储容量。

5.挂载硬件设备

mount命令

mount -a 挂载所有在/etc/fstab 中定义的文件系统

mount -t 指定文件系统的类型(一般不需要)

mount 设备名称 路径

unmount 设备名称或者路径

通常把挂载信息写入到/etc/fstab文件中,格式如下:

设备名称 挂载目录 格式类型 权限选项 是否备份 是否自检

若为1则开机后使用dump进行磁盘备份,为0则不备份

若为1则开机后自动进行磁盘自检,为0则不自检

fdisk命令

fdisk用于管理磁盘分区,格式为 fdisk [磁盘名称]

n 新建磁盘分区

m 查看全部可用的参数

d 删除某个分区信息

w 保存分区信息

p 查看分区信息

q 不保存直接退出

l 列出可用的分区类型

t 改变某个分区的类型

注:如果分区完成后执行 ls /dev/sda1 没有列出任何内容,则需要执行partprobe命令(两次)手动将分区信息同步到内核,如若还是不行,则reboot尝试。

分区完成后,则需要对分区进行格式化。

mkfs.

mkfs.xfs /dev/sda1

最后将存储设备挂载到指定目录即可

mount /dev/sda1 /mnt/

du命令 递归地总结每个文件的磁盘使用量。

du [选项] [文件]

lsblk 查看系统的磁盘

blkid 显示关于可用设备的信息

df命令

显示每个文件所在的文件系统的信息,或默认的所有文件系统。

df [参数] 文件

-t 指定文件类型

-h 列出文件的大小

-Th 显示文件系统的类型格式

?

技术架构演变

随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

单一应用架构

当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。

此时,用于简化增删改查工作量的 数据访问框架(ORM) 是关键。

缺点:随着应用功能的增多,代码量越来越大,越来越难维护,那怎么解决代码一体化的问题?

垂直应用架构

当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。

此时,用于加速前端页面开发的 Web框架(MVC) 是关键。

缺点:垂直架构中相同逻辑代码需要不断的复制,不能复用。每个垂直模块都相当于一个独立的系统。

分布式服务架构

当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。

此时,用于提高业务复用及整合的 分布式服务框架(RPC) 是关键。

缺点:服务越来越多,需要管理每个服务的地址,调用关系错综复杂,难以理清依赖关系,服务状态难以管理,无法根据服务情况动态管理。

流动计算架构

当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。

此时,用于提高机器利用率的 资源调度和治理中心(SOA) 是关键。

缺点:服务间会有依赖关系,一旦某个环节出错会影响较大,服务关系复杂,运维、测试部署困难,不符合DevOps思想。

微服务架构:

单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责

微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。

面向服务:面向服务是说每个服务都要对外暴露服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供 Rest 的接口即可。

自治:自治是说服务间互相独立,互不干扰

团队独立:每个服务都是一个独立的开发团队,人数不能过多。

技术独立:因为是面向服务,提供 Rest 接口,使用什么技术没有别人干涉。

前后端分离:采用前后端分离开发,提供统一 Rest 接口,后端不用再为 PC、移动端开发不同接口。

数据库分离:每个服务都使用自己的数据源

部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护 Docker 部署服务

什么是微服务

Martin Fowler 是这样解释微服务概念的:

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.

翻译如下:

简而言之,微服务架构风格是一种将单个应用程序作为一套小型服务开发的方法,每种应用程序都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。 这些服务是围绕业务功能构建的,可以通过全自动部署机制独立部署。 这些服务的集中管理最少,可以用不同的编程语言编写,并使用不同的数据存储技术。

微服务就是将一个单体架构的应用按业务划分为一个个的独立运行的程序即服务,它们之间通过 HTTP 协议进行通信(也可以采用消息队列来通信,如 RabbitMQ,Kafaka 等),可以采用不同的编程语言,使用不同的存储技术,自动化部署(如 Jenkins)减少人为控制,降低出错概率。服务数量越多,管理起来越复杂,因此采用集中化管理。例如 Eureka,Zookeeper 等都是比较常见的服务集中化管理框架。

微服务是一种架构风格。一个大型的复杂软件应用,由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好的完成该任务。

优点

测试容易

可伸缩性强

可靠性强

跨语言

协同开发

方便系统迭代

缺点

运维成本高,部署项目多

接口兼容版本问题

分布式系统的复杂性

分布式事务

SOA vs MicroServices

面向服务架构(SOA)是一种用于设计软件的伟大原则。在SOA中,所有组件都是独立自主的,并能为其他组件提供服务。大体上,SOA与微服务架构是非常相像的。那么它们之间的区别到底是什么呢?微服务是细粒度的SOA组件。换句话说,某单个SOA组件可以被拆成多个微服务,而这些微服务通过分工协作,可以提供与原SOA组件相同级别的功能,如下图所示。

微服务是细粒度的SOA组件,它们是关注点更窄的轻量级服务。微服务推崇执行的标准(例如HTTP)是人们广泛了解并共同使用的。我们可以通过选择合适的语言或工具来构建某个组件(微服务),除了技术栈与服务规模之外,在SOA与微服务之间还有一个更大的区别:领域模型。在一个基于微服务的软件中,每个微服务应该在本地存储自身管理的数据,并将领域模型分别隔离到单个服务中。而在面向SOA的软件中,数据往往存储在单个大型的数据库中,服务之间会共享领域模型。

SOA

微服务架构

应用程序服务的可重用性的最大化

专注于解耦

系统性的改变需要修改整体

系统性的改变是创建一个新的服务

DevOps和持续交付正在变得流行,但还不是主流

强烈关注DevOps和持续交付

专注于业务功能重用

更重视“上下文边界”的概念

通信使用企业服务总线ESB(Enterprise Service Bus)

对于通信而言,使用较少精细和简单的消息系统

支持多种消息协议

使用轻量级协议,例如HTTP,REST或Thrift API

对部署到它的所有服务使用通用平台

应用程序服务器不是真的被使用,通常使用云平台

容器(如Docker)的使用不太受欢迎

容器在微服务方面效果很好

SOA服务共享数据存储

每个微服务可以有一个独立的数据存储

共同的治理和标准

轻松的治理,更加关注团队协作和选择自由

一句话总结:微服务是 SOA 发展出来的产物,它是一种比较现代化的细粒度的 SOA 实现方式。

Dubbo vs Spring Cloud

Spring 全家桶 用起来很舒服,只有你想不到,没有它做不到。

Dubbo 很多国内的企业还在用,可以支持 RESTful 风格的 API,调用远程 API 像调用本地 API 一样,同时其基于接口的方式增加了服务间的耦合。

对比项

Spring Cloud

Dubbo

服务注册中心

Spring Cloud Netflix Eureka

ZooKeeper

服务调用方式

REST API

RPC

服务网关

Spring Cloud Netflix Zuul

断路由

Spring Cloud Netflix Hystrix

集群容错

分布式配置

Spring Cloud Config

服务跟踪

Spring Cloud Sleuth

消息总线

Spring Cloud Bus

数据流

Spring Cloud Stream

批量任务

Spring Cloud Task

总结

Dubbo 由于是二进制的传输,占用带宽会更少

SpringCloud 是 http 协议传输,带宽会比较多,同时使用 http 协议一般会使用 JSON 报文,消耗会更大

Dubbo 的开发难度较大,原因是 Dubbo 的 jar 包依赖问题很多大型工程无法解决

SpringCloud 的接口协议约定比较自由且松散,需要有强有力的行政措施来限制接口无序升级

Dubbo 的注册中心可以选择 ZooKeeper Redis 等多种,SpringCloud 的注册中心只能用 eureka 或者自研

从系统结构简易程序:SpringCloud 的系统结构更简单、“注册 + SpringMvc=SpringCloud”,而 Dubbo 各种复杂的 url,protocol,register,invocation,dubbofilter,dubboSPI,dubbo序列化..........炫技的成分更多一些

从性能:Dubbo 的网络消耗小于 SpringCloud,但是在国内 95% 的公司内,网络消耗不是什么太大问题,如果真的成了问题,通过压缩、二进制、高速缓存、分段降级等方法,很容易解决。

微服务设计原则

AKF 拆分原则

业界对于可扩展的系统架构设计有一个朴素的理念,就是:通过加机器可以解决容量和可用性问题(如果一台不行就两台)。

用个段子描述就是:世界上没有什么事是一顿烧烤解决不了的,如果有,那就两顿。

这一理念在“云计算”概念疯狂流行的今天。得到了广泛的认可。对于一个规模迅速增长的系统而言。容量和性能问题当然是首当其冲的。但是随着时间的向前,系统规模的增长,除了面对性能与容量的问题外,还需要面对功能与模块数量上增长带来的系统复杂性问题。以及业务变化带来的提供差异化服务问题。而许多系统在架构设计时并未充分考虑到这些问题,导致系统的重构成为常态。从而影响业务交付能力,还浪费人力财力。对此《The Art of Scalability》一书提出了一个更加系统的可扩展模型——AKF 可扩展立方。这个立方体中沿着三个坐标轴设置分别为 X,Y,Z。

一个叫 AKF 的公司的技术专家抽象总结的应用扩展的三个维度。理论上按照这三个扩展模式,可以将一个单体系统,进行无限扩展。

X 轴:指的是水平复制,很好理解,就是讲单体系统多运行几个实例,成为集群加负载均衡的模式。

Z 轴:是基于类似的数据分区,比如一个互联网打车应用突然火了,用户量激增,集群模式撑不住了,那就按照用户请求的地区进行数据分区,北京、上海、四川等多建几个集群。

Y 轴:就是我们所说的微服务的拆分模式,就是基于不同的业务拆分。

场景说明:比如打车应用,一个集群撑不住时,分了多个集群,后来用户激增还是不够用,经过分析发现是乘客和车主访问量很大,就将打车应用拆成了三个,分别为乘客服务、车主服务、支付服务。三个服务的业务特点各不相同,独立维护,各自都可以再次按需扩展。

前后端分离原则

前后端技术分离,可以由各自的专家来对各自的领域进行优化,这样前端的用户体验优化效果更好。

前后端分离模式下,前后端交互界面更清晰,就剩下了接口模型,后端的接口简洁明了,更容易维护。

前端多渠道集成场景更容易实现,后端服务无需变更,采用统一的数据和模型,可以支持多个前端:例如:微信 h5 前端、PC 前端、安卓前端、IOS 前端。

无状态服务

对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的服务被称为有状态服务,反之称为无状态服务。那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。场景说明:例如我们以前在本地内存中建立的数据缓存、Session 缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。

也就是对同一个 url 请求没有上下文关系。举个生活中的例子:

比如空调遥控器,你按上下调整温度时,空调温度设定值会变化,遥控器信号到空调是单向传输。现在空调显示温度 20 度,遥控器 20 度。如果遥控器与空调之间是有状态的,假设你离开空调接收范围调整了遥控器温度,变成 19,那回到范围内你按一次升高一度,基于原先温度状态,遥控器给空调发送一个“提高1度”的指令,就会出现遥控器提高到 20,而空调变成21。如果要空间与空调之前是无状态的,假设你离开空调接收范围调整了遥控器温度,变成 19,那回到范围内你按一次升高一度,遥控器给空调发送一个“设定温度值20”,这样两者最终还是相同的值。

Restful 通信风格

基于无状态通信原则,在这里我们直接推荐一个实践优选的 Restful 通信风格 ,因为他有很多好处:

无状态协议 HTTP,具备先天优势,扩展能力很强。例如需要安全加密时,有现成的成熟方案 HTTPS 可用。

JSON 报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友好。

语言无关,各大热门语言都提供成熟的 Restful API 框架,相对其他的一些 RPC 框架生态更完善。

大家可以通过 查看更多关于 的文章。

本文采用 。

一、建造者模式的定义

大家平时都去过肯德基用餐,那里不变的是炸鸡、汉堡、薯条、可乐等,这些都是一直都有的,不变的,而其它组合是经常变化的,从而生成不同的“套餐”罢了。而建造模式(Builder Pattern)是将一个复杂的对象的构建过程与它的表示分离,使得同样的构建过程构建不同的表示。使用建造者模式对于用户而言只需要关注指定需要建造的类型就可以获得对象,而不需要了解建造的过程以及细节。

建造者模式适用于创建对象需要很多步骤,但是步骤的顺序不是固定不变的。先看一下建造者模式的类图:

建造者模式中的四个重要角色:

产品(Product):要创建的产品类对象

抽象建造者(Builder):规范产品对象的各个组成部分的建造

建造者(Concrete Builder):具体化对象的各个组成部分的创建

调用者(Director):负责保证对象各部分完整创建或按某种顺序创建

二、建造者模式的应用场景

建造者模式适用于一个具有较多的零件的复杂产品的创建过程,由于需求的变化,组成这个复杂产品的各个零件经常猛烈变化,但是它们的组合方式却相对稳定。

建造者模式适用于以下几种场景:

相同的方法,不同的执行顺序,产生的结果也不同

多个部件或零件,装配到一个对象中,产生的结果不同

产品类复杂,或者产品类中调用顺序不同产生不同的作用

初始化对象特别复杂,参数多,而且很多参数都有默认值

2.1 建造者模式的基本写法

我们以公司的技术培训为例,一个完整的技术培训需要由发布培训通知、制作培训PPT、组织员工培训、现场(远程)培训、提交培训问卷等步骤。下面我们用建造模式的代码来简单实现这类场景,首先创建一个技术培训的 TechnicalTraining 产品类 :

接着创建建造者 TrainingBuilder 类,将复杂的构造过程封装起来:

测试main方法:

public static void main(String[] args) {

}

最后来看一下类图:

2.2 建造者模式的链式写法

在平时的应用中,建造者模式通常是采用链式编程的方式构造对象,下面我们来改造上面的案例代码,将TechnicalTraining变成TrainingBuilder的内部类,将构造步骤添加进去,每完成一个步骤,都返回 this:

测试main方法:

最后再来看下类图:

三、建造者模式在源码中的体现

3.1 StringBuilder类

使用StringBuilder类,我们常用的有append()、toString()方法,我们来看下append()方法的源码:

3.2 Spring中的BeanDefinitionBuilder类

比如 BeanDefinitionBuilder 通过调用 getBeanDefinition()方法获得一个 BeanDefinition 对象,比如下面的源码:

四、建造者模式的优缺点

建造者模式的优点:

封装性好,创建和使用分离;

扩展性好,建造类之间独立、一定程度上解耦。

建造者模式的缺点:

产生多余的 Builder 对象;

产品内部发生变化,建造者都要修改,成本较大。

建造者模式和工厂模式的区别

通过前面的学习,我们已经了解建造者模式,那么它和工厂模式有什么区别呢?

1、建造者模式更加注重方法的调用顺序,工厂模式注重于创建对象。

2、创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的都一样。

3、关注重点不一样,工厂模式模式只需要把对象创建出来就可以了,而建造者模式中不仅要创建出这个对象,还要知道这个对象由哪些部件组成。

4、建造者模式根据建造过程中的顺序不一样,最终的对象部件组成也不一样。

?网站:

演示地址:

开发环境技术:B/S(.NET C# )

语言:.net、C++、C#

开发环境:Visual Studio 2012

数据库:sqlserver2005/oracle11h/mysql4.5及以上版本

操作系统:Microsoft Windows Servsr 2008R2及以上

工具简介:

1、帮企业快速地实现各种通用功能,结合系统现有的通用权限管理功能。

2、快速地开发出各种项目应用系统。让企业开发一个系统变得非常轻松。

3、符合RBAC 灵活不仅符合国际通用标准,又能满足国内的大中小型软件项目的灵活设置需求。

4、文档齐全支持二次开发提供完善的接口函数调用说明、开放接口、开放源码、开放数据库结构设计。

5、分层理念 SOA理念程序可以采用不同的实施策略、架构需求、方便维护、方便扩展。

6、有价值且优秀的产品,这样您就有了市场需求了。

7、适用于OA、网站、电子政务、ERP、CRM等基于B/S架构的应用软件系统的快速开发工具。

重写后的 方法体非常简单:返回 name 字段的哈希码。这样的话,put 和 get 用到的哈希码就是相同的,因为“沉默王二”的哈希码是 867758096。再次运行程序,你就会发现输出结果不再是 null 而是 18 了。

03、小结

1) 的作用是用来判断两个对象是否相等。

2) 的作用是获取对象的哈希码;哈希码一般是一个整数,用来确定对象在哈希表(比如 HashMap)中的索引位置。

拿 HashMap 来说,它本质上是通过数组实现的。当我们要获取某个“值”时,实际上是要获取数组中的某个位置的元素。而数组的位置,就是通过“键”来获取的;更进一步说,是通过“键”对应的哈希码计算得到的。

3)如果两个对象需要相等(equals),那么它们必须有着相同的哈希码(hashCode);

4)但如果两个对象有着相同的哈希码,它们却不一定相等。

来对照一下官方给出的关于 和 的解释:

If two objects are equal according to the method, then calling the method on each of the two objects must produce the same integer result.

It is not required that if two objects are unequal according to the method, then calling the method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

可能有读者会问:“一定要同时重写 和 吗?”

回答当然是否定的。如果对象作为键在哈希表中,那么两个方法都要重写,因为 put 和 get 的时候需要用到哈希码和 方法;

如果对象不在哈希表中,仅用来判断是否相等,那么重写 就行了。

嘿嘿

常常有小伙伴推荐罗伯特·C·马丁的《代码整洁之道(Clean Code)》。今天我们就来了解一下这本书,它值不值得一看?

关于此书

《代码整洁之道》出版于2008年,近年来,一直被列为“亚马逊最畅销的五本书”之一。本书作者被亲切地称为“Bob叔叔”,他也是《敏捷宣言》的原作者之一,资历非常丰富。本书在Goodreads上平均评分为4.4(评分人数超13,000)。可以说,这是一本程序员的必读书。

本文将本书精简为五个要点。

1. 尊重抽象

《代码整洁之道》中写到:如果要确保函数仅做一件事,则需要确保每个函数的语句都位于同一抽象层次。

为说明这一点,马丁用了以下示例(出自FitNesse):

public String render() throws Exception

StringBuffer html =new StringBuffer("

if (size >0)

html.append(" size="").append(size + 1).append(""");

html.append(">");

return html.toString();

在GitHub上查看no_abstraction.java源代码

这里至少混合了两个抽象层次。第一个是固定大小的hr标签的高级概念,第二个是处理实际标签构造的低级语法细节。为了说明这一点,对代码进行更清晰地重构,如下所示:

public String render() throws Exception

{

HtmlTag hr =new HtmlTag("hr");

if (extraDashes >0)

hr.addAttribute("size", hrSize(extraDahses));

return hr.html();

}

private String hrSize(int height)

{

int hrSize = Height</span> +1;

return String.format("%d", hrSize);

}

在GitHub上查看abstraction.java源代码

注意:

Render()函数现在仅负责构建hr标签

将构建标签的底层详细信息的任务转给HtmlTag模块

大小格式被抽象为独立的函数

马丁认为:

“分离抽象层次是重构最重要的功能之一,也是最难实现的功能之一。”

当然,在以后的代码中,我会有更多考虑。

2. 整洁代码关乎规则,要花大量精力

我不希望本文仅是列出编写整洁代码的要点和规则。对本书而言这也无甚作用——因为采取教条式的教学方法是远远不够的。

相反,在本书中马丁呼吁发展强烈的个人原则感,且不断说明将“脏代码”变整洁所需的努力和职责。本书将其称为“代码感”,它要求“严格使用艰难获得整洁代码的大量小技巧。”

“整洁代码并非遵循一组规则编写的。不可能因为学习一套金规玉律就成为软件大师。专业精神和手工艺来自于推动规则形成的价值。” —罗伯特·C·马丁(RobertC. Martin)

就个人而言,我没什么自信,所以很喜欢这种说法。就连Bob叔叔都坚信编写代码是一份需要严肃自律的工作,要花费大量精力,真是极大的安慰。为了真正擅于整洁代码,我们需要迭代我们作为程序员的个人开发以及代码的开发。

3. 代码尽量精简

“函数的首要规则是体积小。第二规则是使其尽可能地变小。” ——罗伯特·C·马丁

这里有两个含义:

函数本身应该简短——几乎不超过20行,大多数情况下少于10行

函数应尽可能不要采用参数

简洁函数能增加代码的易读性。这也使我们倾向于编写功能单一高效的函数。

对于类,他也有类似的看法。他建议使用“职责”而非“代码行”来衡量类的大小。即一个类应该只有一个职责。这就是所谓的“单一职责原则”(SRP)。

保持代码简短是“分划”策略,如果一个大文件包含大量冗长而复杂的代码,则可以将该文件分为多个模块,将模块分为多个函数,再将函数分为多个子函数,直至看到代码逻辑和任务。

4. 编程是工艺

我时常认为,将编程喻为建筑和构造并不恰当。因为程序员不会做一个完整的设计,从头开始建基,再一步步搭建直至完工。

编程的步骤是:先画草图,再反复添加细节。程序员要做的是修改、完善和扩展——这些都在各抽象层次上完成,直到软件满足要求为止。而软件永远不会真正完成。

这就是《代码整洁之道》的中心思想。贯穿全书的要点是:软件是一门艺术,做软件就像“画画”。作者认为编程的本质是一门工艺。

但如何让编程从单纯地写代码变成“工艺”呢?

马丁认为,程序员掌握的主要工具是持续重构和测试驱动开发(TDD)。两者像硬币的两面般协同工作。来看一些概念:

重构是在不更改输出的情况下调整现有计算机代码结构的过程。

测试驱动开发是将需求转换为特定测试用例,再添加代码以使测试通过的过程。

因此,制作软件的过程可能如下所示:

编写测试代码以验证所需但未实现的功能。

编写有效代码(可能不整洁),并通过测试。

逐步重构代码(保证每次通过测试),使代码在每次开发迭代中都更加清晰。

“不要想着一次性编程后系统就能正确、漂亮地运行。今日的任务仅仅是让程序运行起来,而重构和扩展系统是明天的任务。这是迭代和增量敏捷的本质。”

——罗伯特·C·马丁

因此,本书的中心思想是,整洁代码是在开发和实践中实现的,而非简单地一口气创建出来。

5. 代码本身清晰易读

注释很少却清晰、表达力强的代码优于注释多的混乱、复杂的代码。” ——罗伯特·C·马丁

在“注释、有意义的命名和格式“一章中,马丁强烈主张代码本身应该清晰易读。示例:

// Check to see if theemployee is eligible for full benefits

if ((employee.flags & HOURLY_FLAG) &&

(employee.age > 65))

将其重构为:

if(employee.isEligibleForFullBenefits())

注意:

删除注释

条件逻辑封装到一个方法中

因为使用的是方法而不是独立函数,因此可以使用实例变量,从而创建调用零参数的方法

给该方法起一个描述性的名称,使其职责更加明确

《代码整洁之道》关于命名写了整整一个章节,本质上是对Tim Ottinger规则的详细说明。包括:

设置可读性高的名称——例如,int elapsedTimeInDays,而不是in days

使用读得出来的名称——例如,客户而不是DtaRcrd102

避免使用编码——不要用前缀m_表示"members",也不要使用匈牙利表示法

每个概念对应一个词——不要fetch,retrieve,get多个概念对应一个词

每天都会有更新看过的朋友可以点波关注,Java学习路线和优质资源评论或后台回复“Java”获取。

结语

《代码整洁之道》中,并非每个想法都是Bob叔叔提出的,他在书中的各部分都承认了这一点。而这反而是使本书如此成功的一个原因——它是编程界智慧的汇聚,并附有实例。

如果要说一个小瑕疵,那就是与高层概念的章节相比,有关底层细节的章节有点少。“系统”章只有13页,仅仅是“注释“章的一半。但是,我怀疑减少对系统的重视,是为了将讨论保留在他后来的书《架构整洁之道(CleanArchitecture)》中。

综合考虑,这真的是目前最好的编程书籍之一,我会把该书放到我的2021年书单中。

(一)存储结构与磁盘划分

文件系统层次化标准(FHS,Filesystem Hierarchy Standard)是根据以往无数Linux系统用户和开发者的经验而总结出来的,是用户在Linux系统中存储文件时需要遵守的规则,用于指导我们应该把文件保存到什么位置,以及告诉用户应该在何处找到所需的文件。

1、一切从“/”开始

Linux系统中的一切文件都是从“根(/)”目录开始的,并按照文件系统层次化标准(FHS)采用树形结构来存放文件。另外,Linux系统中的文件和目录名称是严格区分大小写的,且文件名称中不得包含斜杠(/)。

Linux系统中的文件存储结构如下图所示。

在Linux系统中,最常见的目录以及所对应的存放内容如下表所示。

目录名称

放置文件的内容

/boot

开机所需文件—内核、开机菜单以及所需配置文件等

/dev

★以文件形式存放任何设备与接口

/etc

★服务配置文件

/home

★用户主目录,也可以安装第三方软件。

/bin

存放单用户模式下还可以操作的命令,普通用户执行的命令,存放系统外部命令。

/lib

开机时用到的函数库,以及/bin与/sbin下面的命令要调用的函数。不要动

/sbin

开机过程中需要的命令,系统管理员执行的命令,存放系统内部命令。

/media、/mnt

用于挂载设备文件的目录

/opt

安装第三方的软件

/root

★系统管理员的家目录

/srv

一些网络服务的数据文件目录,默认为空。

/tmp

任何人均可使用的“共享”临时目录。有SBIT保护位。

/proc

虚拟文件系统,例如系统内核、进程、外部设备及网络状态等。不要动

/usr/local

用户自行安装的(第三方)软件

/usr/sbin

Linux系统开机时不会使用到的软件/命令/脚本

/usr/share

帮助与说明文件,也可放置共享文件

/var

★主要存放经常变化的文件,如系统日志。

/lost+found

当文件系统发生错误时,将一些丢失的文件片段存放在这里。ext3和ext4文件系统具有该目录。

绝对路径(absolute path):从“根(/)”目录开始到当前目录的完整路径为绝地路径。

相对路径(relative path):从当前目录开始到指定目录的路径为绝对路径。

2、物理设备的命名规则

磁道:上图中硬盘被一圈圈分成18等分的同心圆,这些同心圆就是磁道,但打开硬盘,用户不能看到这些,它实际上是被磁头磁化的同心圆.这些磁道是有间隔的,因为磁化单元太近会产生干扰。

扇区:每个磁道中被分成若干等份的区域,扇区是硬盘数据存储的最小单位。

柱面:假如一个硬盘只有上图中的3个磁盘片,每一片中的磁道数是相等的,从外圈开始,这些磁道被分成了0磁道、1磁道、2磁道...具有相同磁道编号的同心圆组成面就称作柱面,为了便于理解,柱面可以看作没有底的铁桶,从上图可以看出,柱面数就是磁盘上的磁道数,柱面是硬盘分区的最小单位,因此,一个硬盘的容量=柱面*磁道*扇区*512。

簇:扇区是硬盘数据存储的最小单位,但操作系统无法对数目众多的扇区进行寻址,所以操作系统就将相邻的扇区组合在一起,形成一个簇,然后再对簇进行管理,每个簇可以包括2、4、8、16、32、64个扇区。

linux系统内核中的udev设备管理器会自动把硬件名称规范起来,udev设备管理器的服务会一直以守护进程的形式运行并侦听内核发出的信号来管理/dev目录下的设备文件。

Linux系统中常见的硬件设备文件名称如下表所示。

硬件设备

文件名称

IDE设备

/dev/hd[a-d]

SCSI/SATA/U盘

/dev/sd[a-p]

软驱

/dev/fd[0-1]

打印机

/dev/lp[0-15]

光驱

/dev/cdrom是/dev/sr0的软链接“cdrom -> sr0”

鼠标

/dev/mouse

磁带机

/dev/st0或/dev/ht0

系统采用a~z来代表不同的硬盘(默认从a开始分配,超过26个后从aa、ab…az),如28块硬盘标记如下:

[root@linuxprobe ~]# ls /dev/sd[a-z][0-9] && ls /dev/sd[a-z][a-z][0-9]

/dev/sda1 /dev/sdc1 /dev/sdg1 /dev/sdk1 /dev/sdo1 /dev/sds1 /dev/sdw1 /dev/sdaa1

/dev/sda2 /dev/sdd1 /dev/sdh1 /dev/sdl1 /dev/sdp1 /dev/sdt1 /dev/sdx1 /dev/sdab1

/dev/sdb1 /dev/sde1 /dev/sdi1 /dev/sdm1 /dev/sdq1 /dev/sdu1 /dev/sdy1

/dev/sdb2 /dev/sdf1 /dev/sdj1 /dev/sdn1 /dev/sdr1 /dev/sdv1 /dev/sdz1

硬盘分区编号原则:

主分区或扩展分区的编号范围1-4(1-4中的任意1个数字必须为扩展分区,其它为主分区,主分区编号可以不用完);

逻辑分区编号从5开始……。

举例1:

Device Boot Start End Blocks Id System

/dev/sdb1 2048 10487807 5242880 83 Linux

/dev/sdb2 10487808 14682111 2097152 83 Linux

/dev/sdb3 16779264 18876415 1048576 5 Extended

/dev/sdb5 16781312 16986111 102400 83 Linux

/dev/sdb6 16988160 17008639 10240 83 【SSR机场】 Linux

如:/dev/sda5设备文件名称包含如下信息。

/dev/目录中保存的是硬件设备文件;其次sd表示是存储设备;然后a表示系统中同类接口中第一个被识别到的设备,最后5表示这个设备是一个逻辑分区。即“/dev/sda5”表示“这是系统中第一块被识别到的“SCSI/SATA/U盘”硬件设备中分区编号为5的逻辑分区的设备文件”。

硬盘设备是由大量的扇区组成的,每个扇区的容量为512字节。第一个512字节的扇区保存着主引导记录MBR与分区表信息。主引导记录占用446字节,分区表占用64字节(4个主分区*16字节/个),结束符占用2字节;其中分区表中每记录一个分区信息就需要16字节,最多只有4个分区信息可以写到第一个扇区中,这4个分区就是4个主分区或扩展分区。

第一个扇区中的数据信息如下图所示。

为了解决分区个数不够的问题,可将第一个扇区的4个分区表中拿出来一个16字节(原本要写入主分区信息)的空间指向另外一个分区即扩展分区(包含除主分区外的其它全部磁盘空间),扩展分区其实并不是一个真正的分区,而是一个包含多个逻辑分区的集合。这样一来,用户一般会选择使用3个主分区加1个扩展分区的方法,然后在扩展分区中创建出多个逻辑分区,从而满足多分区(大于4个)的需求。

如上图所示,主分区创建完创建扩展分区时需要把剩余空间均配置为扩展分区,然后在扩展分区上再创建多个逻辑分区,如下图。

注:扩展分区无法挂载使用,如下图。

3、文件系统与数据资料

用户在硬件存储设备中执行的文件建立、写入、读取、修改、转存与控制等操作都是依靠文件系统来完成的。文件系统的作用是合理规划硬盘,以保证用户正常的使用需求。Linux系统最常见的文件系统如下所示。

Ext2(RHEL4):

Ext3(RHEL5):是一款日志文件系统,能够在系统异常宕机时避免文件系统资料丢失,并能自动修复数据的不一致与错误。缺点:当硬盘容量较大时,所需的修复时间也会很长,而且也不能百分之百地保证资料不会丢失。原理:把整个磁盘的每个写入动作的细节都预先记录下来,以便在发生异常宕机后能回溯追踪到被中断的部分,然后尝试进行修复。

Ext4(RHEL6):Ext3的改进版本,作为RHEL6系统中的默认文件管理系统,存储容量高达1EB(1EB=1,073,741,824GB),且能够有无限多的子目录。另外,Ext4文件系统能够批量分配block块,从而极大地提高了读写效率。

XFS(RHEL7):是一种高性能的日志文件系统,而且是RHEL 7中默认的文件管理系统,它的优势在发生意外宕机后尤其明显,即可快速地恢复可能被破坏的文件,且强大的日志功能只用花费极低的计算和存储性【SSR机场】能。它最大可支持的存储容量为18EB,这几乎满足了所有需求。

Linux系统中有一个名为super block的“硬盘地图”,在里面记录着整个文件系统的信息。

[root@linuxprobe ~]# blkid //查看使用的文件系统类型

/dev/sdb1 on /haha type ext4 (rw,relatime,seclabel,data=ordered)

/dev/sda1 on /boot type xfs (rw,relatime,seclabel,attr2,inode64,noquota)

[root@linuxprobe ~]# df -T //查看使用的文件系统类型

Filesystem Type 1K-blocks Used Available Use% Mounted on

/dev/sdb1 ext4 5029504 20472 4730504 1% /haha

/dev/sr0 iso9660 3654720 3654720 0 100% /mnt/cdrom

/dev/sda1 xfs 508588 121216 387372 24% /boot

[root@linuxprobe ~]# lsblk -f /dev/sdb //查看使用的文件系统类型

NAME FSTYPE LABEL UUID MOUNTPOINT

sdb

├─sdb1 ext4 d4056cf4-b61f-480e-8b3c-286af91c77b3 /haha

├─sdb2 swap b33176d6-df62-4a2d-a630-3c05d0af7fbe [SWAP]

└─sdb4 xfs d933dfe2-ec48-4dc6-87f5-976d3e14c773 /hehe

[root@linuxprobe ~]# xfs_info /dev/sda1 //查看xfs文件系统信息

meta-data=/dev/sda1 isize=2【小火箭加速器】56 agcount=4, agsize=32000 blks

= sectsz=512 attr=2, projid32bit=1

= crc=0

data= bsize=4096 blocks=128000, imaxpct=25

= sunit=0 swidth=0 blks

naming=version 2 bsize=4096 ascii-ci=0 ftype=0

log=internal bsize=4096 blocks=853, version=2

= sectsz=512 sunit=0 blks, lazy-count=1

realtime=none extsz=4096 blocks=0, rtextents=0

[root@linuxprobe ~]# tune2fs -l /dev/sdb1 //查看ext4文件系统信息

tune2fs 1.42.9 (28-Dec-2013)

Filesystem volume name:

Last mounted on:

Filesystem UUID: d4056cf4-b61f-480e-8b3c-286af91c77b3

Filesystem magic number: 0xEF53

Filesystem revision #: 1 (dynamic)

Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize

Filesystem flags: signed_directory_hash

Default mount options: user_xattr acl

Filesystem state: clean

Errors behavior: Continue

Filesystem OS type: Linux

Inode count: 327680

Block count: 1310720

Reserved block count: 65536

Free blocks: 1252258

Free inodes: 327669

First block: 0

Block size: 4096

Fragment size: 4096

Group descriptor size: 64

Reserved GDT blocks: 639

Blocks per group: 32768

Fragments per group: 32768

Inodes per group: 8192

Inode blocks per group: 512

Flex block group size: 16

Filesystem created: Sat Feb 29 20:29:11 2020

Last mount time: Sun Mar 1 09:48:26 2020

Last write time: Sun Mar 1 09:48:26 2020

Mount count: 6

Maximum mount count: -1

Last checked: Sat Feb 29 20:29:11 2020

Check interval: 0 ()

Lifetime writes: 213 MB

Reserved blocks uid: 0 (user root)

Reserved blocks gid: 0 (group root)

First inode: 11

Inode size: 256

Required extra isize: 28

Desired extra isize: 28

Journal inode: 8

Default directory hash: half_md4

Directory Hash Seed: bba4f0e2-9fb5-4433-b4c2-584d8409129e

Journal backup: inode blocks

Linux把每个文件的权限与属性记录在inode表格(占用一个block块)中,每个文件占用一个独立的inode表格,一个inode表格默认大小仅为128B(Ext3),记录一个block信息占用4B,而文件的实际内容则保存在block块中(大小可以是1KB、2KB或4KB);每个文件的inode表格被写满后,Linux系统会自动分配出一个block块,专门用于像inode那样记录其他block块的信息,这样把各个block块的内容串到一起,就能够让用户读到完整的文件内容了。

inode表格里面记录着如下信息:

该文件的访问权限(read、write、execute);

该文件的所有者与所属组(owner、group);

该文件的大小(size);

该文件的创建或内容修改时间(ctime);

该文件的最后一次访问时间(atime);

该文件的修改时间(mtime);

文件的特殊权限(SUID、SGID、SBIT);

该文件的真实数据地址(point)。

对于存储文件内容的block块,有下面两种常见情况(以4KB的block大小为例进行说明)。

情况1:文件很小(1KB),但依然会占用一个block,因此会潜在地浪费3KB。

情况2:文件很大(5KB),那么会占用两个block(5KB-4KB后剩下的1KB也要占用一个block)。

Linux系统支持数十种文件系统,每种文件系统底层操作肯定有很多区别,但是为了让用户在读取和写入文件是不用关心底层的硬盘结构,Linux内核中的软件层为用户程序提供了一个VFS接口(Virtual File System),这个接口封装了底层文件系统的特性和细节,这样用户实际上在操作文件时就是统一对这个VFS进行操作,从而不必关心每种文件系统的不同。

VFS架构示意图如下:

4、硬盘分区、格式化操作

拿到一块新的硬盘存储设备后,需要先分区、格式化文件系统,然后才能挂载并正常使用。

4.1)fdisk命令

fdisk命令用于管理磁盘分区,提供集添加、删除、转换分区等功能于一身的“一站式分区服务”。

命令格式:fdisk [磁盘名称]

同步分区信息命令:partprobe

格式化分区:mkfs.xfs 设备文件 或 mkfs.ext4 设备文件

fdisk命令中交互式的参数及作用:

参数

作用

m

查看全部可用的参数

n

添加新的分区

d

删除某个分区

l

列出所有可用的分区类型

t

改变某个分区的类型

p

查看分区表信息

w

保存并退出

q

不保存直接退出

举例1:

[root@linuxprobe ~]# fdisk /dev/sdb //执行分区操作

Welcome to fdisk (util-linux 2.23.2).

Changes will remain in memory only, until you decide to write them.

Be careful before using the write command.

Command (m for help): p //查看分区表信息

Disk /dev/sdb: 21.5 GB, 21474836480 bytes, 41943040 sectors

Units=sectors of 1 * 512=512 bytes

Sector size (logical/physical): 512 bytes / 512 bytes

I/O size (minimum/optimal): 512 bytes / 512 bytes

Disk label type: dos

Disk identifier: 0xe332ce96

Device Boot Start End Blocks Id System

/dev/sdb1 2048 10487807 5242880 83 Linux

/dev/sdb2 10487808 14682111 2097152 83 Linux

/dev/sdb4 14682112 16779263 1048576 5 Extended

/dev/sdb5 14684160 14888959 102400 83 Linux

Command (m for help): d //删除一个分区

Partition number (1,2,4,5, default 5): 5

Partition 5 is deleted

Command (m for help): n //新一个分区

Partition type:

p primary (2 primary, 1 extended, 1 free)

l logical (numbered from 5)

Select (default p): p

Selected partition 3

First sector (16779264-41943039, default 16779264):

Using default value 16779264

Last sector, +sectors or +size{K,M,G} (16779264-41943039, default 41943039):

Using default value 41943039

Partition 3 of type Linux and of size 12 GiB is set

Command (m for help): w //保存并退出

The partition table has been altered!

Calling ioctl() to re-read partition table.

WARNING: Re-reading the partition table failed with error 16: Device or resource busy.

The kernel still uses the old table. The new table will be used at

the next reboot or after you run partprobe(8) or kpartx(8)

Syncing disks.

[root@linuxprobe ~]#mkfs.xfs /dev/sdb1 //执行普通应用分区的格式化

[root@linuxprobe ~]#mkswap /dev/sdb2 //执行作为专用交换分区的格式化

分区格式化完成后需要挂载方能使用,具体操作详见“6、挂载设备文件”小节。

4.2)du命令

du命令用于查看文件数据占用空间大小,即查看一个或多个文件占用了多大的硬盘空间。

命令格式:du -sh [文件或目录]

举例1:

[root@linuxprobe ~]# du -sh /home/* //查看home目录下所有一级目录分别占用的空间大小。

128M /home/EPEL

123M /home/EPEL-linuxprobe.com.tar.bz2

24K /home/linuxprobe

17M /home/LinuxProbe.pdf

[root@linuxprobe ~]# du -sh /home //查看home目录占用总的空间大小。

267M /home

5、添加交换分区

SWAP(交换)分区是一种通过在硬盘中预先划分一定的空间,然后把内存中暂时不常用的数据临时存放到硬盘中,以腾出物理内存空间让更活跃的程序服务来使用的技术。其设计目的是为了解决真实物理内存不足的问题,只有当真实的物理内存耗尽后才会调用交换分区的资源。在生产环境中,交换分区的大小一般为真实物理内存的1.5~2倍。

首先通过fdisk命令进行分区操作,分区完成后通过SWAP分区专用的格式化命令mkswap对新建的主分区或逻辑分区进行格式化操作(扩展分区不行):

命令格式:mkswap 设备文件名

使用swapon命令把准备好的SWAP分区设备正式挂载到系统中。

命令格式:swapon 设备文件名

举例1:

为了能够让新的交换分区设备在重启后依然生效,需要按下面的格式将相关信息写入到/etc/fstab配置文件中并保存(交换分区一个挂载点可以挂载多个设备文件)。

6、挂载设备文件

分区格式化完成后需要将格式化后的设备文件与一个已存在的目录文件进行关联即挂载。挂载和撤销挂载主要用到两个命令:mount、umount。

6.1)mount命令

mount命令用于挂载文件系统。

命令格式:mount [参数] 设备文件 挂载目录。注:“设备文件”需要先格式化成文件系统。

参数:-a自动检查、挂载所有在/etc/fstab中定义的文件系统;-t指定文件系统的类型,一般不需要使用-t参数来指定文件系统的类型,Linux系统会自动进行判断。

举例1:

[root@linuxprobe ~]# mount /dev/sdb1 /haha/

[root@linuxprobe ~]# df -hT

Filesystem Type Size Used Avail Use% Mounted on

/dev/sda1 xfs 497M 119M 379M 24% /boot

/dev/sdb1 ext4 4.8G 20M 4.6G 1% /haha

把挂载信息按照指定的填写格式:“设备文件 挂载目录 格式类型 权限选项 是否备份 是否自检”写入到/etc/fstab文件中实现永久自动挂载。文件中各位置参数含义如下表。

字段

释意

设备文件

一般为设备的路径+设备名称,也可以写唯一识别码(UUID,Universally Unique Identifier,通过blkid命令查看UUID)

格式:“UUID=0026f461-b0a2-4d90-8304-7cb4d0575432”。

挂载目录

指定要挂载到的目录,需在挂载前创建好。一个挂载点只能挂载一个设备文件。

格式类型

指定文件系统的格式,比如Ext3、Ext4、XFS、SWAP、iso9660(此为光盘设备)等。

权限选项

若设置为defaults,则默认权限为:rw, suid, dev, exec, auto, nouser, async。

是否备份

使用dump备份工具,若为1则开机后使用dump进行磁盘备份,为0则不备份。

是否自检

使用fsck文件系统扫描检查工具,若为1则开机后自动进行磁盘自检,为0则不自检。

根文件系“/”统对应该字段的值应该为1,其他文件系统应该为2,若文件系统无需在启动时扫描检查则设置该字段为0。

举例1:

[root@linuxprobe ~]# cat /etc/fstab

# /etc/fstab

# Created by anaconda on Sun Feb 16 14:06:24 2020

# Accessible filesystems, by reference, are maintained under '/dev/disk'

# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info

#

/dev/mapper/rhel-root / xfs defaults 1 1

UUID=0026f461-b0a2-4d90-8304-7cb4d0575432 /boot xfs defaults 1 2

/dev/mapper/rhel-swap swap swap defaults 0 0

/dev/sr0 /mnt/cdrom iso9660 defaults 0 0

/dev/sdb1 /haha ext4 defaults 0 0

/dev/sdb2 swap swap defaults 0 0

6.2)umount命令

umount命令用于撤销已经挂载的设备文件。

命令格式:umount [设备文件 或 挂载目录]

举例1:

[root@linuxprobe ~]# umount /dev/sdb1

[root@linuxprobe ~]# umount /haha

1.添加数据库函数

CwDatabase.h

CwDatabase.cpp

2.实现,获取到测试层所有实体,并改为红色

Commands.h

Commands.cpp

注册命令

3.打开AutoCAD,新建图层“测试层”,画任意实体,执行命令,可以看到所有实体变为红色

我正在寻找一些好的工具/脚本,使我能够从git存储库中生成一些统计信息。 我已经在某些代码托管网站上看到了此功能,其中包含诸如...的信息。

每位作者提交

每天/每周/每年/等等的提交次数。

随着时间的推移行的代码

图表

... 多得多

基本上,我只是想知道我的项目随着时间的推移会增长多少,哪个开发人员会提交大多数代码,等等。

#1楼

我正在用ruby做一个git仓库统计信息生成器,它叫做git_stats 。

您可以在项目页面上找到为某些存储库生成的示例。

以下是其功能的列表:

一般统计

文件总数(文本和二进制)

总行数(添加和删除)

总提交

s

活动(总计和每位作者)

按日期提交

按一天的时间提交

按星期几提交

按一周的小时提交

按一年中的月份提交

每年提交

按年和月提交

s

由作者提交

作者添加的行

作者删除的行

行由作者更改

文件和行

按日期

通过扩展

如果您有任何想法要增加或改进,请告诉我,我们将不胜感激。

#2楼

如果您的项目在GitHub上,那么您现在(2013年4月)拥有Pulse (请参见“ 快速掌握Pulse ”):

它更加受限制,并且不会显示您可能需要的所有统计信息,但是可随时用于任何GitHub项目。

Pulse是发现项目近期活动的好方法。

Pulse将向您显示谁一直在积极提交以及项目的默认分支中发生了哪些更改 :

您可以在导航栏的左侧找到链接 。

请注意,还没有API可以提取该信息。

#3楼

快速的Google搜索将我引导至: http : //gitstats.sourceforge.net/

你尝试过这个项目吗? 我确定有类似的项目。

#4楼

除了xyld提到的GitStats ( git历史统计生成器 ) 之外 ,它是用Python编写的,并且需要使用Gnuplot来绘制图形,此外还有

gitstat ( SourceForge )项目( 基于Web的git statistics interface ),用PHP和Perl编写,

Git Statistics ,又名gitstats( 旨在收集有关git存储库统计信息的指标框架 ),用Python编写, 是Google Summer of Code 2008的git-statistics项目的结果。这不是一个网络应用

gitinspector是一个相当新的基于CLI的Python工具,用于生成漂亮的报告

Hercules-使用Go编写的无依赖项的本机应用程序,专门研究高级分析类型。

#5楼

每位作者提交

1.背景

在完成一个分表项目后,发现分表的数据迁移后,新库所需的存储容量远大于原本两张表的大小。在做了一番查询了解后,完成了优化。

回过头来,需要进一步了解下为什么会出现这样的情况。

与标题的问题的类似问题还有,为什么表数据内容删除了而表大小没有变化。其本质都是一样的。

要回答这些问题,我们需要从mysql的索引模型谈起。

2.InnoDB 的索引模型

由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以接下来就以 InnoDB 为例,分析其中的索引模型。

在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。而InnoDB中,使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的,每一个索引会对应一颗B+树。

假设,我们有一个主键列为 ID 的表,表中有字段 k,并且在 k 上有索引,建表语句如下

表中 R1~R5 的 (ID,k) 值分别为 (10,1)、(20,2)、(30,3)、(50,5) 和 (70,7),索引id和索引k的B+树的示例示意图如下。

根据叶子节点的内容,索引类型分为主键索引和非主键索引,主键索引的叶子节点存的是整行数据R1~R5,非主键索引的叶子节点内容是主键的值。

从图中可以看出,基于非主键索引的查询需要多扫描一棵索引树才能找到对应的数据。

提一句题外话,我们在应用中应该尽量使用主键查询。

3.索引维护

B+ 树为了维护索引有序性,在增删改数据的时候需要做必要的维护。

假设,我们要删掉 R4 这个记录,InnoDB 引擎只会把 R4 这个记录标记为删除。如果之后要再插入一个 ID 在 300 和 600 之间的记录时,可能会复用这个位置。

如果删掉了一个数据页上的所有记录,那么整个数据页就能被复用了。进一步地,如果我们用 delete 命令把整个表的数据删除呢?结果就是,这个表相关的所有的数据页都会被标记为可复用。

但是,无论如何,磁盘文件的大小并不会缩小。

这些被标记为可复用,而并没有实际被使用的空间,就是一些“存储空洞”。

实际上,不止是删除数据会造成空洞,插入数据也会。

以上图为例,如果插入新的行 ID 值为 80,则只需要在 R5 的记录后面插入一个新记录。

如果新插入的 ID 值为 60,就相对麻烦了,需要逻辑上挪动后面的数据,空出位置。

而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。

除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,插入一条记录竟然使得整体空间利用率降低大约 50%。

可以看到,由于 page 2 满了,再插入一个 ID 是 60 的数据时,就不得不再申请一个新的页面 page 3 来保存数据了。

页分裂完成后,page 2 的末尾就留下了空洞(注意:实际上,可能不止 1 个记录的位置是空洞)。

另外,更新索引上的值,可以理解为删除一个旧的值,再插入一个新值。不难理解,这也是会造成空洞的。

因此,大量的增删改之后的表,都是可能存在很大的“数据空洞”的。

因此,我们就能解释,为什么分表后的总存储变大了。

因为分表后,需要从老库全量同步数据到新库,数据同步平台开启多个线程进行同步,插入各个分表并不是按照递增的顺序插入的,因此,会产生巨量的“数据空洞”,造成存储空间变大。

如果能够把这些空洞去掉,就能达到收缩表空间的目的。而重建表就能达到这样的目的。

4.重建表

如果我们手动重建一张表,可以新建一个与表 A 结构相同的表 B,然后按照主键 ID 递增的顺序,把数据一行一行地(就是递增地)从表 A 里读出来再插入到表 B 中。由于表 B 是新建的表,所以表 A 主键索引上的空洞,在表 B 中就都不存在了。显然地,表 B 的主键索引更紧凑,数据页的利用率也更高。如果我们把表 B 作为临时表,数据从表 A 导入表 B 的操作完成后,用表 B 替换 A,从效果上看,就起到了收缩表 A 空间的作用。

这里,你可以使用 alter table A engine=InnoDB 命令来重建表。在 MySQL 5.5 版本之前,这个命令的执行流程跟我们前面描述的差不多,区别只是这个临时表 B 不需要你自己创建,MySQL 会自动完成转存数据、交换表名、删除旧表的操作。显然,花时间最多的步骤是往临时表插入数据的过程,如果在这个过程中,有新的数据要写入到表 A 的话,就会造成数据丢失。因此,在整个 DDL 过程中,表 A 中不能有更新。也就是说,这个 DDL 不是 Online 的。

MySQL 5.6 版本开始引入的 Online DDL,对这个操作流程做了优化。

建立一个临时文件,扫描表 A 主键的所有数据页;

用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;

生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中;

临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件;(应用row log的过程可能又回有页分裂)

用临时文件替换表 A 的数据文件。

可以看到,在这个过程中,由于日志文件记录和重放操作这个功能的存在,这个方案在重建表的过程中,允许对表 A 做增删改操作。这也就是 Online DDL 名字的来源。

需要补充说明的是,上述的这些重建方法都会扫描原表数据和构建临时文件。对于很大的表来说,这个操作是很消耗 IO 和 CPU 资源的。因此,如果是线上服务,你要很小心地控制操作时间。

optimize table、analyze table 和 alter table 这三种方式重建表的区别:

从 MySQL 5.6 版本开始,alter table t engine=InnoDB(也就是 recreate)默认的就是上面online DDL 的流程了;

analyze table t 其实不是重建表,只是对表的索引信息做重新统计,没有修改数据,这个过程中加了 MDL 读锁;

optimize table t 等于 recreate+analyze。

参考内容:

丁奇《MySQL 45讲》

看到这里了,原创不易,点个关注、点个赞吧,你最好看了~

知识碎片重新梳理,构建Java知识图谱:历史文章查阅非常方便)

扫码关注我的公众号“阿丸笔记”,第一时间获取最新更新。同时可以免费获取海量Java技术栈电子书、各个大厂面试题。

炮台网页小游戏制作

游戏简介:场景内放置一个固定的炮台,可以根据鼠标转动调整视角,进而从不同角度完成炮弹射击;另一端放置任意数量木箱,设为打击目标,并在上方设定计数器,记录炮弹发射数量。

所使用到的素材图片

背景图层

炮弹

炮管

木箱

制作过程

1.新建game层,双击然后选择tiled background, 选择准备好的背景图片素材,随后添加physic行为,并修改行为属性,如图:

2.添加ball图层,为其添加physic属性并设置,如图:

3.拖入素材(木箱) ,添加physic属性并设置,如图:

4.在操作界面双击,添加mouse事件

(1):在event sheet面板单击右键选择add event,新添加system是,点击every tick设置炮台的角度

(2):让炮弹初始状态为隐匿状态

(3):添加事件,打到点击鼠标,炮台发射炮弹,并且设置炮弹发射的距离和视角

(4):在every tick上添加text(用来实现记录炮弹发射数量)

(5):添加制作人信息。

完成界面:

运行展示:

1.新建hadoop用户,家目录下创建app,并上传服务器jdk、hadoop压缩包;

注意:修改hadoop用户家目录下app、两个压缩包的权限问题,使用chmod;以及所有者与所有组的修改 chown,chgrp等命令,我就是在遇到的问题,因为不熟悉linux,因此搭建hadoop搭建了好几遍。

先安装jdk

2. 解压到app目录

3.修改环境变量

sudo vi /etc/profile

shift+g 跳转最后一行

4. source /etc/profile

安装hadoop

5.解压缩到app目录

6. cd 到 /etc/hadoop/ ll

7.配置hadoop-env.sh

8.配置core-site.xml

9.配置hdfs-site.xml

10.配置mapred-site.xml

11.配置yarn-site.xml

12.将hadoop添加到环境变量

sudo vi /etc/profile

13. source /etc/profile

14.找到sbin下,start-dfs.sh启动

原题: 给你一个数组 nums,对于其中每个元素 nums[i],请你统计数组中比它小的所有数字的数目。换而言之,对于每个 nums[i] 你必须计算出有效的 j 的数量,其中 j 满足 j !=i 且 nums[j] < nums[i] 。以数组形式返回答案。

说明: 由于要求返回的数组的排序与原数组一一对应,个人认为用暴力解法是比较好的选择。当然也不仅有一种方法,不过还需使用额外的空间存储原数组的顺序。

直接for循环,每次取其中一位,与数组中的其他数进行对比,并记录。

微信小程序

注册

由于发文限制,请自行到微信公众平台注册

项目结构

project.config.json 配置文件(不需要动)

app.json(用户配置)

路由pages

window 整个程序样式

tabar底部设置

.....

app.js 入口程序

xxx.js

app.js是入口程序,代表应用要做的事

通过路由切换到了index.js中

Page(options)

data 属于页面的数据

this.data.xxx

onLoad: 获取路由参数 , onLoad(options.xxx)

onReady: 更改页面数据,比如改title

onShow: 多次触发,切换页面的时候

onHide: 多次触发

data初始化数据

onReady 修改数据

修改并更新视图

常用组件

view视图

text文本框

picker 选择列表

input组件(无法使用font-family)

通过bind事件(全小写),来绑定对应js中声明的函数,赋值赋函数引用

在函数内部,接受e这个对象

e.detail.value 获取value值

e.target.id 元素id

e.target.dataset 获取元素上的属性

wx指令

在页面中 wx-xxx

wx:if="" 是否显示

wx:for=""

wx:for="{{数组}}"

元素:{{item}} 索引 {{index}}

条件 wx:if="{{布尔值}}" 相邻 wx:elif="{{布尔值}}" 相邻 wx:else

事件

change 改变事件

tap 点击事件

bindblur 离焦

bindconfirm 回车

bindinput 键入

checkbox-group 包裹checkbox 获取的detail.value是一个数组

表单

button的formType

submit

reset

发请求

wx.request( options)

url

success

fail

complete

引入资源

html导入

js导入

css导入 @import "common.wxss";

路由参数 :

图片预览wx.preview

本地存储 wx.setStorageSync

上拉下拉

上拉和下拉的操作需要触发,必须js文件中,声明其对应的回调函数

下拉函数 onPullDownRefresh -> 需要配置xxx.json

上拉触底 onReachBottom

停止下拉的函数 wx.stopPullDownRefresh()

以上函数可以通过Page构造编辑器自动输出,单页面属性参考配置window

导航

url=page

open-type:

默认: navigateTo :非tabBar页面。

switchTab tabBar 页面。

navigateBack 返回

reLaunch 再运行

编程导航: wx这个全局对象提供了一套 open-type对应值的api

wx.switchTab||navigateTo 注意区分非tabbar

tabbar

color selectedColor 对于整个tabbar的设置

iconPath selectedIconPath 每个不同的单独设置

有趣的功能

播放下载音乐

弹幕

查找元素

引入资源

html导入

js导入

css导入 @import "common.wxss";

路由参数 :

图片预览wx.preview

本地存储 wx.setStorageSync

上拉下拉

上拉和下拉的操作需要触发,必须js文件中,声明其对应的回调函数

下拉函数 onPullDownRefresh -> 需要配置xxx.json

上拉触底 onReachBottom

停止下拉的函数 wx.stopPullDownRefresh()

__以上函数可以通过Page构造编辑器自动输出,单页面属性参考配置window

组件

创建一个compoment

配置其.json文件 component:true

在使用组件的.json文件中定义

usingComponents:{ 名:路径}

在使用组件的页面使用 名

接收数据

组件wxml的slot

在组件的wxml中可以包含 节点,用于承载组件使用者提供的wxml结构。

默认情况下,一个组件的wxml中只能有一个slot。需要使用多slot时,可以在组件js中声明启用。

此时,可以在这个组件的wxml中使用多个slot,以不同的 来区分。

使用时,用 属性来将节点插入到不同的slot上。

样式

尽可能使用class,组件可以指定它所在节点的默认样式,使用 选择器

使用插件

进入官方后台添加插件,获取appid等信息

进入app.json文件中配置plugins:{ "名":{ 相关信息 } }

根据插件文档使用插件(有可能是组件) 需配置usingComponent

登录

简介

上一步已经完成登录,使用security默认提供的用户名和密码登录,这一章我们实现自定义用户名和密码登录。

处理用户信息获取逻辑

security默认处理用户信息获取逻辑,要想实现自己的逻辑那就要新建自己的逻辑类然后去实现security默认逻辑

在browser项目新建MyUserDetailsService类去实现UserDetailsService重写loadUserByUsername方法

我们先打开返回值UserDatails源码看看里面有什么方法

getAuthorities() #权限信息

getPassword() #密码

getUsername() #用户名

isAccountNonExpired() #你的账户是否过期 返回true没有过期 返回false已过期

isAccountNonLocked() #你的账号是否锁定 返回true没有被锁定 返回false已经被锁定 用户可以申请恢复

isCredentialsNonExpired() #你的密码是否过期 返回true没有过期 返回false已过期

isEnabled() #你的账号是否被删除 返回true未删除 返回false已删除 一般数据库伪删除不能用户不能申请恢复

修改loadUserByUsername方法去定义自己的用户名和密码:

security提供的两种User构造方法:

返回:用户名,密码,用户权限资料。账号是否被删除,账户是否过期,密码是否过期,账号是否锁定默认都是true

返回:用户名,密码,账号是否被删除,账户是否过期,密码是否过期,账号是否锁定,用户权限资料

public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities)

直接测试下面这个返回值的方法:

启动项目测试,不管密码是否正确都会提示账号已被锁定。

修改accountNonLocked为true再重启项目:

启动以后输入用户名和密码点登陆发现报异常,这个异常是spring security5+后密码策略变更了。必须使用 PasswordEncoder 方式也就是你存储密码的时候

需要使用这样的方式,这个在官网文档中有讲到。由于我们没有使用加密策略等等我们解决这个问题。

修改passaword值为{noop}123456,再启动项目:

错误密码:

正确密码:

处理密码加密解密

创建BrowserSecurityConfig类去继承WebSecurityConfigurerAdapter类

在UserDetailsService中我们模拟存入数据库中的密码就是加密后的字符串

框架会把提交的密码使用我们定义的passwordEncode加密后调用

org.springframework.security.crypto.password.PasswordEncoder#matches方法,与返回的User中的密码进行比对。配对正常就验证通过;

启动项目测试:

jdk class 类型的应用在很多框架中被大量运用,例如mybatis、spring等

Type:是java语言中所有类型的父类接口;

ParameterizedType:参数化类型接口;

GenericArrayType:泛型数组类型接口

TypeVariable:变量类型接口

GenericDeclaration:泛型定义接口

Class:应用运行中的所有类和接口的类实例

WIldcardType:通配符类型

以上只是针对每个接口类型做了阐述,或许有很多描述不够准确,欢迎拍砖指正;

现在写一个比较简单的测试类,来体现部分类型接口的使用场景

上面的例子比较容易理解,后续会将框架中的使用例子加以补充!!!

1.前言

我们使用Linux服务器开发Python程序,一般都建议使用Ubuntu系统。现在的ubuntu系统都自带了python2.7版,而有时工作需要Python3。

众所周知,Python2和Python3在语法上是不兼容的, ,但是如果开发的程序必须使用Python2而不能使用Python3,这时候就不得不再下载一个Python2, 那这时候环境变量的设置就会出现冲突,此时使用Anaconda就可以完美的解决这些问题。

2.Anaconda简介

Anaconda 是一个Python的发行版,包括了Python和很多常见的软件库, 和一个包管理器Conda。常见的科学计算类的库都包含在里面了,使得安装比常规Python安装要容易。

主要是!装了Anaconda就不需要单独装python了,因为Anaconda就是用来管理我们不同版本的python环境的。

对整个Python环境, 最关键的是需要有一个解释器, 和一个包集合,所有的第三方包都放在site-packages文件夹里面。比如说爬虫脚本用到了第三方的requests包,而另一台计算机是刚刚是装好原始python的, 也就是说根本没有任何第三方包,那么这个爬虫脚本是无法在另一台机器上运行的(因为需要requests包的支持)。

3.安装

3.1.下载

首先从官网下载对应的AnaConda版本。

下载链接

历史版本

这里下载版本为:Anaconda3-2019.10-Linux-x86_64.sh

3.2.安装

修改文件权限

安装

3. 确认许可

4. 选择安装目录

5. 设置环境变量

3.3.验证是否成功

先使环境变量生效

检测 python

出现介绍了python的版本信息,而且后面带了Anaconda的标识,这就说明Anaconda安装成功。

which python

使用的是Anaconda目录下的Python,说明安装成功。

检测Conda

输出conda 4.7.12 之类的版本信息,就说明环境变量设置成功

更多文章,请关注:

总体结构

安装了node, webpack 、vue-cli 等工具之后,打开VS Code控制台

vue init webpack 命令创建了基于vue-cli2.0的项目

1、build文件夹:

1.1、build.js

'use strict'

require('')() //调用版本检查

process.env.NODE_ENV='production' //将环境配置为生产环境

const ora=require('ora') //npm包 loading插件

const rm=require('rimraf') //npm包 用于删除文件

const path=require('path')//npm包 文件路径工具

const chalk=require('chalk')//npm包 在终端输出带颜色的文字

const webpack=require('webpack')//引入webpack.js

const config=require('')//引入配置文件

const webpackConfig=require('')//引入生产环境配置文件

// 在终端显示loading效果,并输出提示

const spinner=ora('building for production...')

spinner.start()

//先递归删除dist文件再生成新文件,避免冗余

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err=> {

if (err) throw err

webpack(webpackConfig, (err, stats)=> {

spinner.stop()

if (err) throw err

process.stdout.write(stats.toString({

colors: true,

modules: false,

children: false,

chunks: false,

chunkModules: false

}) + '\n\n')

if (stats.hasErrors()) {

console.log(chalk.red(' Build failed with errors.\n'))

process.exit(1)

}

console.log(chalk.cyan(' Build complete.\n'))

console.log(chalk.yellow(

' Tip: built files are meant to be served over an HTTP server.\n' +

' Opening index.html over file:// won\'t work.\n'

))

})

})

ps:require/export是一种nodeJs(commonJs规范)的依赖注入的方法,import/export是ES6语法,用于引入模块,在nodeJs中使用的ES6语法最终会使用babel工具(babel-loader)转化为ES5

1.2、check-version.js:检测node和npm的版本,实现版本依赖

'use strict'

const chalk=require('chalk')

const semver=require('semver')//检查版本

const packageConfig=require('')

const shell=require('shelljs')//shelljs 模块重新包装了 child_process,调用系统命令更加方便

function exec (cmd) {//返回通过child_process模块的新建子进程,执行 Unix 系统命令后转成没有空格的字符串

return require('child_process').execSync(cmd).toString().trim()

}

const versionRequirements=[

{

name: 'node',

currentVersion: semver.clean(process.version),//使用semver格式化版本

versionRequirement: packageConfig.engines.node //获取package.json中设置的node版本

}

]

if (shell.which('npm')) {

versionRequirements.push({

name: 'npm',

currentVersion: exec('npm --version'),// 自动调用npm --version命令,并且把参数返回给exec函数,从而获取纯净的版本号

versionRequirement: packageConfig.engines.npm

})

}

module.exports=function () {

const warnings=[]

for (let i=0; i < versionRequirements.length; i++) {

const mod=versionRequirements[i]

//若版本号不符合package.json文件中指定的版本号,就报错

if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {

warnings.push(mod.name + ': ' +

chalk.red(mod.currentVersion) + ' should be ' +

chalk.green(mod.versionRequirement)

)

}

}

if (warnings.length) {

console.log('')

console.log(chalk.yellow('To use this template, you must update following to modules:'))

console.log()

for (let i=0; i < warnings.length; i++) {

const warning=warnings[i]

console.log(' ' + warning)

}

console.log()

process.exit(1)

}

}

1.3、utils.js:utils是工具的意思,是一个用来处理css的文件,这个文件包含了三个工具函数:

生成静态资源的路径

生成 ExtractTextPlugin对象或loader字符串

生成 style-loader的配置

var path=require('path')// node自带的文件路径工具

var config=require('')// 配置文件

var ExtractTextPlugin=require('extract-text-webpack-plugin')// 提取css的插件

exports.assetsPath=function (_path) {

var assetsSubDirectory=process.env.NODE_ENV==='production'

? config.build.assetsSubDirectory

: config.dev.assetsSubDirectory

//nodeJs path提供用于处理文件路径的工具;path.posix提供对路径方法的POSIX(可移植性操作系统接口)特定实现的访问(可跨平台); path.posix.join与path.join一样,不过总是以 posix 兼容的方式交互

return path.posix.join(assetsSubDirectory, _path)

}

exports.cssLoaders=function (options) {

options=options || {}

var cssLoader={

loader: 'css-loader',

options: {

minimize: process.env.NODE_ENV==='production',

sourceMap: options.sourceMap

}

}

function generateLoaders (loader, loaderOptions) {

var loaders=[cssLoader]

if (loader) {

loaders.push({

loader: loader + '-loader',

options: Object.assign({}, loaderOptions, {

sourceMap: options.sourceMap

})

})

}

// ExtractTextPlugin提取css(当上面的loaders未能正确引入时,使用vue-style-loader)

if (options.extract) {// 生产环境中,默认为true

return ExtractTextPlugin.extract({

use: loaders,

fallback: 'vue-style-loader'

})

} else {//返回vue-style-loader连接loaders的最终值

return ['vue-style-loader'].concat(loaders)

}

}

return {

css: generateLoaders(),//需要css-loader 和 vue-style-loader

postcss: generateLoaders(),//需要css-loader、postcssLoader 和 vue-style-loader

less: generateLoaders('less'),//需要less-loader 和 vue-style-loader

sass: generateLoaders('sass', { indentedSyntax: true }),//需要sass-loader 和 vue-style-loader

scss: generateLoaders('sass'),//需要sass-loader 和 vue-style-loader

stylus: generateLoaders('stylus'),//需要stylus-loader 和 vue-style-loader

styl: generateLoaders('stylus')//需要stylus-loader 和 vue-style-loader

}

}

exports.styleLoaders=function (options) {

var output=[]

var loaders=exports.cssLoaders(options)

//将各种css,less,sass等综合在一起得出结果输出output

for (var extension in loaders) {

var loader=loaders[extension]

output.push({

test: new RegExp('\\.' + extension + '$'),

use: loader

})

}

return output

}

1.4、vue-loader.conf.js:处理.vue文件,解析这个文件中的每个语言块(template、script、style),转换成js可用的js模块。

'use strict'

const utils=require('')

const config=require('')

const isProduction=process.env.NODE_ENV==='production'

//生产环境,提取css样式到单独文件

const sourceMapEnabled=isProduction

? config.build.productionSourceMap

: config.dev.cssSourceMap

module.exports={

loaders: utils.cssLoaders({

sourceMap: sourceMapEnabled,

extract: isProduction

}),

cssSourceMap: sourceMapEnabled,

cacheBusting: config.dev.cacheBusting,

//编译时将“引入路径”转换为require调用,使其可由webpack处理

transformToRequire: {

video: ['src', 'poster'],

source: 'src',

img: 'src',

image: 'xlink:href'

}

}

1.5、webpack.base.conf.js:开发、测试、生产环境的公共基础配置文件,配置输出环境,配置模块resolve和插件等

'use strict'

const path=require('path')// node自带的文件路径工具

const utils=require('')// 工具函数集合

const config=require('')// 配置文件

const vueLoaderConfig=require('')// 工具函数集合

function resolve(dir) {

return path.join(__dirname, '..', dir)

}

module.exports={

context: path.resolve(__dirname, ''),

//入口js文件(默认为单页面所以只有app一个入口)

entry: {

app: ''

},

//配置出口

output: {

path: config.build.assetsRoot,//打包编译的根路径(dist)

filename: '[name].js',

publicPath: process.env.NODE_ENV==='production'

? config.build.assetsPublicPath

: config.dev.assetsPublicPath//发布路径

},

resolve: {

extensions: ['.js', '.vue', '.json'],// 自动补全的扩展名

//别名配置

alias: {

'vue$': 'vue/dist/vue.esm.js',

'@': resolve('src'),// eg:"src/components"=> "@/components"

}

},

module: {

rules: [

//使用vue-loader将vue文件编译转换为js

{

test: /\.vue$/,

loader: 'vue-loader',

options: vueLoaderConfig

},

//通过babel-loader将ES6编译压缩成ES5

{

test: /\.js$/,

loader: 'babel-loader',

include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]

},

//使用url-loader处理(图片、音像、字体),超过10000编译成base64

{

test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,

loader: 'url-loader',

options: {

limit: 10000,

name: utils.assetsPath('img/[name].[hash:7].[ext]')

}

},

{

test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,

loader: 'url-loader',

options: {

limit: 10000,

name: utils.assetsPath('media/[name].[hash:7].[ext]')

}

},

{

test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,

loader: 'url-loader',

options: {

limit: 10000,

name: utils.assetsPath('fonts/[name].[hash:7].[ext]')

}

}

]

},

//nodeJs全局变量/模块,防止webpack注入一些nodeJs的东西到vue中

node: {

setImmediate: false,

dgram: 'empty',

fs: 'empty',

net: 'empty',

tls: 'empty',

child_process: 'empty'

}

}

1.6、webpack.dev.conf.js:webpack配置开发环境中的入口

'use strict'

const utils=require('')

const webpack=require('webpack')

const config=require('')

const merge=require('webpack-merge')//webpack-merge实现合并

const path=require('path')

const baseWebpackConfig=require('')

const CopyWebpackPlugin=require('copy-webpack-plugin')

const HtmlWebpackPlugin=require('html-webpack-plugin')

const FriendlyErrorsPlugin=require('friendly-errors-webpack-plugin')//webpack的提示错误和日志信息的插件

const portfinder=require('portfinder')// 查看空闲端口位置,默认情况下搜索8000这个端口

const HOST=process.env.HOST

const PORT=process.env.PORT && Number(process.env.PORT)

const devWebpackConfig=merge(baseWebpackConfig, {

module: {

rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })

},

devtool: config.dev.devtool,//调试模式

devServer: {

clientLogLevel: 'warning',

historyApiFallback: {//使用 HTML5 History API 时, 404 响应替代为 index.html

rewrites: [

{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },

],

},

hot: true,//热重载

contentBase: false, // 提供静态文件访问

compress: true,//压缩

host: HOST || config.dev.host,

port: PORT || config.dev.port,

open: config.dev.autoOpenBrowser,//npm run dev 时自动打开浏览器

overlay: config.dev.errorOverlay

? { warnings: false, errors: true }

: false,// 显示warning 和 error 信息

publicPath: config.dev.assetsPublicPath,

proxy: config.dev.proxyTable,//api代理

quiet: true, //控制台打印警告和错误(用FriendlyErrorsPlugin 为 true)

watchOptions: {// 检测文件改动

poll: config.dev.poll,

}

},

plugins: [

new webpack.DefinePlugin({

'process.env': require('')

}),

new webpack.HotModuleReplacementPlugin(),//模块热替换插件,修改模块时不需要刷新页面

new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.

new webpack.NoEmitOnErrorsPlugin(),//webpack编译错误的时候,中断打包进程,防止错误代码打包到文件中

// 将打包编译好的代码插入index.html

new HtmlWebpackPlugin({

filename: 'index.html',

template: 'index.html',

inject: true

}),

// 提取static assets 中css 复制到dist/static文件

new CopyWebpackPlugin([

{

from: path.resolve(__dirname, ''),

to: config.dev.assetsSubDirectory,

ignore: ['.*']//忽略.*的文件

}

])

]

})

module.exports=new Promise((resolve, reject)=> {

portfinder.basePort=process.env.PORT || config.dev.port

portfinder.getPort((err, port)=> { //查找端口号

if (err) {

reject(err)

} else {

//端口被占用时就重新设置evn和devServer的端口

process.env.PORT=port

devWebpackConfig.devServer.port=port

// npm run dev成功的友情提示

devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({

compilationSuccessInfo: {

messages: [`Your application is running here:

},

onErrors: config.dev.notifyOnErrors

? utils.createNotifierCallback()

: undefined

}))

resolve(devWebpackConfig)

}

})

})

1.7、webpack.dev.prod.js:webpack配置生产环境中的入口

'use strict'

const path=require('path')

const utils=require('')

const webpack=require('webpack')

const config=require('')

const merge=require('webpack-merge')

const baseWebpackConfig=require('')

const CopyWebpackPlugin=require('copy-webpack-plugin')

const HtmlWebpackPlugin=require('html-webpack-plugin')

const ExtractTextPlugin=require('extract-text-webpack-plugin')

const OptimizeCSSPlugin=require('optimize-css-assets-webpack-plugin')

const UglifyJsPlugin=require('uglifyjs-webpack-plugin')

const env=require('')

const webpackConfig=merge(baseWebpackConfig, {

module: {

rules: utils.styleLoaders({

sourceMap: config.build.productionSourceMap,

extract: true,

usePostCSS: true

})

},

devtool: config.build.productionSourceMap ? config.build.devtool : false,//是否开启调试模式

output: {

path: config.build.assetsRoot,

filename: utils.assetsPath('js/[name].[chunkhash].js'),

chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')

},

plugins: [

new webpack.DefinePlugin({

'process.env': env

}),

new UglifyJsPlugin({//压缩js

uglifyOptions: {

compress: {

warnings: false

}

},

sourceMap: config.build.productionSourceMap,

parallel: true

}),

new ExtractTextPlugin({//提取静态文件,减少请求

filename: utils.assetsPath('css/[name].[contenthash].css'),

allChunks: true,

}),

new OptimizeCSSPlugin({//提取优化压缩后(删除来自不同组件的冗余代码)的css

cssProcessorOptions: config.build.productionSourceMap

? { safe: true, map: { inline: false } }

: { safe: true }

}),

new HtmlWebpackPlugin({ //html打包压缩到index.html

filename: config.build.index,

template: 'index.html',

inject: true,

minify: {

removeComments: true,//删除注释

collapseWhitespace: true,//删除空格

removeAttributeQuotes: true//删除属性的引号

},

chunksSortMode: 'dependency'//模块排序,按照我们需要的顺序排序

}),

new webpack.HashedModuleIdsPlugin(),

new webpack.optimize.ModuleConcatenationPlugin(),

new webpack.optimize.CommonsChunkPlugin({ // node_modules中的任何所需模块都提取到vendor

name: 'vendor',

minChunks (module) {

return (

module.resource &&

/\.js$/.test(module.resource) &&

module.resource.indexOf(

path.join(__dirname, '')

)===0

)

}

}),

new webpack.optimize.CommonsChunkPlugin({

name: 'manifest',

minChunks: Infinity

}),

new webpack.optimize.CommonsChunkPlugin({

name: 'app',

async: 'vendor-async',

children: true,

minChunks: 3

}),

new CopyWebpackPlugin([//复制static中的静态资源(默认到dist里面)

{

from: path.resolve(__dirname, ''),

to: config.build.assetsSubDirectory,

ignore: ['.*']

}

])

]

})

if (config.build.productionGzip) {

const CompressionWebpackPlugin=require('compression-webpack-plugin')

webpackConfig.plugins.push(

new CompressionWebpackPlugin({

asset: '[path].gz[query]',

algorithm: 'gzip',

test: new RegExp(

'\\.(' +

config.build.productionGzipExtensions.join('|') +

')$'

),

threshold: 10240,

minRatio: 0.8

})

)

}

if (config.build.bundleAnalyzerReport) {

const BundleAnalyzerPlugin=require('webpack-bundle-analyzer').BundleAnalyzerPlugin

webpackConfig.plugins.push(new BundleAnalyzerPlugin())

}

module.exports=webpackConfig

2、config文件夹:

2.1、dev.env.js和prod.env.js:分别配置:开发环境和生产环境。这个可以根据公司业务结合后端需求配置需要区分开发环境和测试环境的属性

'use strict'

const merge=require('webpack-merge')

const prodEnv=require('')

module.exports=merge(prodEnv, {

NODE_ENV: '"development"'

})

ps:webpack-merge用于实现合并类似于ES6的Object.assign()

'use strict'

module.exports={

NODE_ENV: '"production"'

}

(*注意属性值要用“‘’”双层引住),访问(获取值)时直接用:

process.env.属性名

ps:process(进程)是nodejs的一个全局变量,process.env 属性返回一个用户环境信息的对象

2.2、index.js配置解析:

'use strict';

const path=require('path');

module.exports={

//===================开发环境配置

dev: {

assetsSubDirectory: 'static',//静态资源文件夹(一般存放css、js、image等文件)

assetsPublicPath: '/',//根目录

proxyTable: {},//配置API代理,可利用该属性解决跨域的问题

host: 'localhost', // 可以被 process.env.HOST 覆盖

port: 3030, // 可以被 process.env.PORT 覆盖

autoOpenBrowser: true,//编译后自动打开浏览器页面 "port + host",默认"false"),设置路由重定向自动打开您的默认页面

errorOverlay: true,//浏览器错误提示

notifyOnErrors: true,//跨平台错误提示

poll: false, //webpack提供的使用文件系统(file system)获取文件改动的通知devServer.watchOptions(监控文件改动)

devtool: 'cheap-module-eval-source-map',//webpack提供的用来调试的模式,有多个不同值代表不同的调试模式

cacheBusting: true,// 配合devtool的配置,当给文件名插入新的hash导致清除缓存时是否生成source-map

cssSourceMap: true //记录代码压缩前的位置信息,当产生错误时直接定位到未压缩前的位置,方便调试

},

//========================生产环境配置

build: {

index: path.resolve(__dirname, ''),//编译后"首页面"生成的绝对路径和名字

assetsRoot: path.resolve(__dirname, ''),//打包编译的根路径(默认dist,存放打包压缩后的代码)

assetsSubDirectory: 'static',//静态资源文件夹(一般存放css、js、image等文件)

assetsPublicPath: '/',//发布的根目录(dist文件夹所在路径)

productionSourceMap: true,//是否开启source-map

devtool: '#source-map',//(详细参见:

productionGzip: false,//是否压缩

productionGzipExtensions: ['js', 'css'],//unit的gzip命令用来压缩文件(gzip模式下需要压缩的文件的扩展名有js和css)

bundleAnalyzerReport: process.env.npm_config_report //是否开启打包后的分析报告

}

};

3、node_modules文件夹:

存放npm install时根据package.json配置生成的npm安装包的文件夹

4、src文件夹:

我们需要在src文件夹中开发代码,打包时webpack会根据build中的规则(build规则依赖于config中的配置)将src打包压缩到dist文件夹在浏览器中运行

(1)assets文件:用于存放静态资源(css、image),assets打包时路径会经过webpack中的file-loader编译(因此,assets需要使用绝对路径)成js

(2)components文件夹:用来存放 .vue 组件(实现复用等功能,如:过滤器,列表项等)

(3)router文件夹:在router/index.js文件中配置页面路由

(4)App.vue:是整个项目的主组件,所有页面都是通过使用开放入口在App.vue下进行切换的(所有的路由都是App.vue的子组件)

(5)main.js:入口js文件(全局js,你可以在这里:初始化vue实例、require/import需要的插件、注入router路由、引入store状态管理)

5、static文件夹:

webpack默认存放静态资源(css、image)的文件夹,与assets不同的是:static在打包时会直接复制一个同名文件夹到dist文件夹里(不会经过编译,可使用相对路径)

6、其他文件:

(1).babelrc:浏览器解析的兼容配置,该文件主要是对预设(presets)和插件(plugins)进行配置,因此不同的转译器作用不同的配置项,大致可分为:语法转义器、补丁转义器、sx和flow插件

(2).editorconfig:用于配置代码格式(配合代码检查工具使用,如:ESLint,团队开发时可统一代码风格),这里配置的代码规范规则优先级高于编辑器默认的代码格式化规则 。

(3).gitignore:配置git提交时需要忽略的文件

(4)postcssrc.js: autoprefixer(自动补全css样式的浏览器前缀);postcss-import(@import引入语法)、CSS Modules(规定样式作用域)

(5)index.html:项目入口页面,编译之后所有代码将插入到这来

(6)package.json:npm的配置文件(npm install根据package.json下载对应版本的安装包)

(7)package.lock.json:npm install(安装)时锁定各包的版本号

(8)README.md:项目使用说明

上帝的磨盘转动很慢,但是却磨得很细。 ——毛姆

本文已经收录至我的GitHub,欢迎大家踊跃star 和 issues。

数据结构的基本概念

数据结构

相互之间存在一种或多种特定关系的数据元素的集合,我总结一下就是描述数据关系的一种载体。

数据结构包括逻辑结构和存储结构两个层次的描述。

逻辑结构

描述数据逻辑关系的一种方式,与数据的存储无关。逻辑结构中数据元素之间的关系主要分为四种:集合结构、线性结构、树结构、图结构。所有的数据结构在逻辑上都可以用这四种中的一种。

存储结构

数据和数据元素逻辑关系的存储对象,也被称为物理结构。通常逻辑结构包含两种,链式存储和顺序存储。顺序存储 数据元素存储在一块连续的内存空间上,例如数组,就是一块连续的空间。链式存储 数据存储不一定在一块连续的内存空间上,例如单链表。

数据类型

是一组值的集合和定义在这个集合上的操作的总称。

抽象数据类型

由用户定义的表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称,具体包括三部分,数据对象、数据对象上关系的集合以及对数据对象基本操作的集合。

抽象数据类型有自己的定义格式:

算法与数据结构

算法 解决一类问题而规定的一个有限长操作序列。

算法必须满足几个特性才能称之为算法:

有穷性:算法执行必须是在有限的步数之后完成,执行每一步也是有限的时间。简单来说就是执行一个算法时间是有限,总不能执行一个算法和时间同步没有尽头吧。

确定性:算法每种执行操作都是确定的,执行结果也是确定的,没有二义性的。算法的执行者和阅读者都明确其算法含义和如何执行。

可行性:这个理解起来很简单,算法被设计出来是可以被计算机完成的。

输入&输出:一个算法一定是有输入和输出的,输入是算法执行的条件,输出是算法产生的结果。

算法优劣的评价标准

评价算法优劣的主要从以下几个方面考虑:

正确性:在合理的数据输入下,能够在有限的运行时间内得到正确的结果。

可读性:包括两个方面一个是研究算法的人们易与读写,另一个是执行算法的机器可以执行。

健壮性:对于非法输入,好的算法是会做相应的处理而不是产生一些奇怪的结果。

高效性:高效性包括时间和空间两个方面,在保证算法结果正确的情况下,时间上花费的越少,空间上花费的越少,算法就很高效。现实中往往是二者不可兼得,很多算法都是时间上优越,空间上浪费,还有很多算法反正。

时间复杂度

用算法中的"基本语句" 的执行次数来度量算法的工作量。正常状态下一般用循环或者递归的运行次数。

在某些算法中算法的时间复杂度会根据算法的初始状态决定,这种时候需要计算出算法的最好时间复杂度、最坏时间复杂度和平均时间复杂度。比如常见的排序算法就有最好最坏和平均时间复杂度。

空间复杂度

算法在运行过程中占用的辅助空间大小,被称作该算法的空间复杂度。

时间复杂度和空间复杂度都是用大写的 "O" 表示。对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂度时, 可能会导致占用较多的存储空间, 即可能会使空间复杂度的性能变差, 反之亦然。不过, 通常情况下, 鉴于运算空间较为充足, 人们都以算法的时间复杂度作为算法优劣的衡址指标。

自己在写算法时一定要可以去留意算法的效率问题,不然你写出来的算法虽然满足可行性、确定性、健壮性,也会是一个很烂的算法。时间复杂度是我们日常编程设计考虑最多的。

在学习算法效率的时候一般会把O(3N)≈O(N),N的常数倍都直接约等于O(N)。这也是约等于,不是完全相等。实际编程设计时特别是在一些效率要求较高的程序设计一定要考虑进去,不能约等于。在高并发的请求下,O(3N)和O(N)是有着天壤之别的。

我在工作中遇到的一个实例,差点背了事故。一个高并发的场景下(qps在5k左右),我写了一个O(3N)的程序,测试时逻辑没问题,结果没问题,没有对该场景进行高并发压测,就上线了。上线之后不到十分钟我收到短信报警,多台机器CPU打满了,内存也在飙升(32C—124G的服务器)。此时的我吓坏了,意识到我刚刚发布了,肯定和我发布有关。保证线上优先,立马把刚上线的服务下掉,别影响其他业务正常。下掉我的服务,CPU资源报警解除了。

经过一番review代码,各种测试,最终定位到两个问题。一个是我代码里面有一处内存泄漏导致内存飙升了,还有一处就是时间复杂度的问题。错误的把O(3N)=O(N)的算法上线了。把算法优化为O(N)之后,经过一番压力测试完全没问题。这次事件对我一个很大的启示是,高并发的场景下,O(3N)≠O(N),一定不能等于。

高并发场景下算法的效率尤为重要,此时时间和空间的平衡关系一定要充分考虑。

总结

概念性的东西一般在实际工作中不会去过多纠结,很多工作很久的同学完全不记得这些概念的文字却依然可以轻松愉快的完成相应知识的工作。是的,不拘泥于概念,却熟练运用概念对应的知识是我们的目的。这样说并不是概念不重要,完全不需要看概念。概念是认识一个事物的开始,他表示一个事物是什么,后面的做什么,为什么,都是建立在是什么的基础上的,所以概念一定要理解,而不是背书。

在学校的同学会养成一种很不好的习惯,就是必须去记这些概念。为什么呢?因为考试会考。是的,我在大学的时候也会去记这些概念的文字应付考试。比如下面这些考题就曾经出现在考卷中

简述下列概念:数据、数据元素、数据项、数据对象、数据结构、逻辑结构、存储结构、

抽象数据类型。

不要慌,理解记忆这些概念。既能应付考试,又能很好的理解知识。你需要记住考试好不代表你对知识掌握的好,掌握是指的能把这些知识运用在实际工作和生活应用中。

下一篇文章会写数据结构一种非常重要的数据结构——线性表。记得关注我,精彩内容不错过喔。

微信搜索 龙跃十二 即可订阅,微信更新会早于博客喔。

提供一个批量下载UNPKG文件的小工具

码云:

  

概念定义

  

  Spring Cloud 是一个服务治理平台,提供了一些服务框架。包含了:服务注册与发现、配置中心、消息中心 、负载均衡、数据监控等等。

  Spring Cloud 是一个微服务框架,相比 Dubbo 等 RPC 框架,Spring Cloud 提供了全套的分布式系统解决方案。

  Spring Cloud 对微服务基础框架 Netflix 的多个开源组件进行了封装,同时又实现了和云端平台以及 Spring Boot 框架的集成。

  Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具,它为开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。

  Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud 就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud 做为大管家需要管理好这些微服务,自然需要很多小弟来帮忙。

  

子项目

  

  Spring Cloud 包含了很多子项目:

  

Spring Cloud Netflix 第一代

  

  Netflix是一家美国公司,在美国、加拿大提供互联网随选流媒体播放,定制DVD、蓝光光碟在线出租业务。该公司成立于1997年,总部位于加利福尼亚州洛斯盖图,1999年开始订阅服务。2009年,该公司可提供多达10万部DVD电影,并有1千万的订户。2007年2月25日,Netflix宣布已经售出第10亿份DVD。HIS一份报告中表示,2011年Netflix网络电影销量占据美国用户在线电影总销量的45%。

  针对多种 Netflix 组件提供的开发工具包,其中包括 Eureka、Hystrix、Ribbon、Zuul、Archaius 等。

:一个基于 Rest 服务的服务治理组件,包括服务注册中心、服务注册与服务发现机制的实现,实现了云端负载均衡和中间层服务器的故障转移。

:容错管理工具,实现断路器模式,通过控制服务的节点,从而对延迟和故障提供更强大的容错能力。

:客户端负载均衡的服务调用组件。

:基于 Ribbon 和 Hystrix 的声明式服务调用组件。

:微服务网关,提供动态路由,访问过滤等服务。

:配置管理 API,包含一系列配置管理 API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。

  

Spring Cloud Alibaba 第二代

  

  Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。

  依托 Spring Cloud Alibaba,只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。

:阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

:面向分布式服务架构的轻量级流量控制产品,把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。

:Apache Dubbo? 是一款高性能 Java RPC 框架。

:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。

:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。

:阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。

:阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。

:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。

  

常用组件

  

:服务注册中心。

:客户端负载均衡。

:服务容错保护。

:声明式服务调用。

:OpenFeign 是 Spring Cloud 在 Feign 的基础上支持了 Spring MVC 的注解,如 @RequesMapping等等。OpenFeign 的 @FeignClient 可以解析 SpringMVC 的 @RequestMapping 注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

:API 网关服务,过滤、安全、监控、限流、路由。

:Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

:分布式配置中心。配置管理工具,支持使用 Git 存储配置内容,支持应用配置的外部化存储,支持客户端配置信息刷新、加解密配置内容等。

:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与 Spring Cloud Config 联合实现热部署。

:消息驱动微服务。

:分布式服务跟踪。

:阿里巴巴结合自身微服务实践,开源的微服务全家桶。在 Spring Cloud 项目中孵化,很可能成为Spring Cloud 第二代的标准实现。

  

总结

  

Spring Cloud 第一代

Spring Cloud 第二代

网关

Spring Cloud Zuul

Spring Cloud Gateway

注册中心

Eureka,Consul,ZooKeeper

阿里 Nacos,拍拍贷 Radar 等可选

配置中心

Spring Cloud Config

阿里 Nacos,携程 Apollo,随行付 Config Keeper

客户端负载均衡

Ribbon

spring-cloud-commons 的 Spring Cloud LoadBalancer

熔断器

Hystrix

spring-cloud-r4j(Resilience4J),阿里 Sentinel

  虽然 Eureka,Hystrix 等不再继续开发或维护,但是目前来说不影响使用,不管怎么说感谢开源,向 Netflix 公司的开源致敬。

  

版本说明

  

  

为什么 Spring Cloud 版本用的是单词而不是数字?

  

  这样设计的目的是为了更好的管理每个 Spring Cloud 的子项目的清单。避免总版本号与子项目的版本号混淆。

  例如:Spring Cloud 2.2.0.RELEASE 的 Spring Cloud Netflix 2.2.2.RELEASE 如果使用这种方式会让开发者混淆版本号。

  

定义规则

  

  采用伦敦的地铁站名称来作为版本号的命名,根据首字母排序,字母顺序靠后的版本号越大。

  Spring 官方详细的版本查看接口:

  

发布计划

  

版本号

版本

说明

BUILD-XXX

开发版

开发团队内部使用

M

里程碑版

MileStone,M1 表示第 1 个里程碑版本,一般同时标注 PRE,表示预览版

RC

候选发布版

Release Candidate,正式发布版的前一个观察期,不添加新功能,主要着重于除错

SR

正式发布版

Service Release,SR1 表示第 1 个正式版本,一般同时标注 GA,表示稳定版本

GA

稳定版

经过全面测试并可对外发行称之为GA(General Availability)

  

子项目版本说明

  

  例如:Spring Cloud Alibaba 2.1.0.RELEASE

2:主版本号。当功能模块有较大更新或者整体架构发生变化时,主版本号会更新。

1:次版本号。次版本表示只是局部的一些变动。

0:修改版本号。一般是 bug 的修复或者是小的变动。

RELEASE:希腊字母版本号。标注当前版本的软件处于哪个开发阶段。

  

希腊字母版本说明

  

Base:设计阶段。只有相应的设计没有具体的功能实现。

Alpha:软件的初级版本。存在较多的 bug。

Bate:表示相对 Alpha 有了很大的进步,消除了严重的 bug,还存在一些潜在的 bug。

Gamma:是 Beta 版做过一些修改,成为正式发布的候选版本(Release Candidate)

Release:该版本表示最终版。

  大家可以通过 查看更多关于 的文章。

  本文采用 。

  

   本章节到这里就结束了,喜欢的话就点赞

年后复工大戏,“删库跑路”这个词又登上热搜,更是直接给公司带来数10亿的市值蒸发损失!

说实话,开始以为是程序员之间调侃的段子。可这次不是别人,正是微信生态的第三方服务商微盟,在这个"远程办公”的节骨眼出事了。

1

靠给微信公众账号提供营销推广服务发家,做到上市的微盟怎么也没想到,2020年给自己上的第一课是公司核心运维人员的“删库跑路”。

一石激起千层浪,此时微盟给出了一份解释。

2月25日,微盟集团(SEHK:02013)发布公告称,SaaS生产环境及数据遭到员工“人为破坏”导致公司当前暂时无法向客户提供SaaS产品。

微盟预计,老用户数据将在2月28日晚上24点前方可完成数据修复。这意味着,微盟的老用户将面临超过5天的系统宕机。对疫情期间本来正在经受门店歇业重创的商家来说,可以说是致命性的打击。

一位商家表示:微盟系统崩溃后,已售出的几百万元提货卡无法提货,“客户说我是骗子,微盟销售说是腾讯出问题,腾讯修复又不可能这么慢。店铺数据如果都没了,我的损失该怎么办?”

2

基于微盟的商家小程序也都随之宕机,一度无法打开。从23日晚间起,线上生意基本停摆的商家不在少数,同时像林清玄、百草味、森马等多个知名商家小程序也均已无法打开超过24小时。

据林清轩创始人孙来春的自述,337家门店、2000员工,每月基础开支在3000-4000万之间,初一到初七业绩暴跌95%。正是通过数字化转型,以小程序、电商等方式,林清轩2020年2月累计店均业绩增长85.3%。

但现在打开微信公众号“林清轩山茶焕肤修复专家”,其小程序商城则提示处于“系统更新维护中,预计回复时间2月25日12:00。”

受删库影响的还有刚刚卖身百事的百草味。

枣业第一股好想你收购百草味时,这个休闲零食品牌还处于亏损之中。2013-2015年Q3,百草味获客成本占到销售成本61.4%、69.8%、68.8%,一度被人视为在给电商平台和快递公司打工。但这些年借助微信生态、智慧门店等升级,百草味也曾传出18天卖出1000万、转化率翻一番等好消息。

我们无法得知百草味小程序的营收占比。只是粗略估算下来:按照微盟提出的最迟2月28日晚上24点修复承诺,加上已经停摆的24小时,百草味因小程序宕机问题造成的损失额可能在数百万级。

所有的微盟商家无一例外的都受到了此次系统宕机的影响。经验证,就连微盟官方小程序“微盟微商城”如今也无法使用,打开页面之后显示着“request:ok 重新加载”。

3

这件删库事件到底是谁的锅?那些电商平台数据安全又该如何保障?

由上述可见,客户的数据是存储在服务提供方中心化存储服务器上的,那么备份就显得尤为重要。既然是做此类服务的专业服务商,那就应该有考虑到数据本身的重要程度,分布式存储的灾备方案也应该被考虑周全,可为什么最终还是发生这种糟糕的情况呢?

因为人的问题。

犯罪嫌疑人是微盟研发中心运维部核心运维人员贺某,贺某于2月23日晚18点56分通过个人VPN登入公司内网跳板机,因个人精神、生活等原因对微盟线上生产环境进行了恶意的破坏。

我们再换一个角度,对于专业度要求极高的数据存储运维团队来说,核心的运维也不应该一个人拥有登录所有灾备服务器的权限,这是安全制度的漏洞。可是这种事情却偏偏发生在我们生活当中,直接导致300万商户受到直接影响。

失误已造成,损失已存在,接下来就是解决方案。就目前微盟删库情况的出现,要回到微信商城系统搭建的起点进行分析。

其实搭建微信商城平台部署方式无非就是两种,SaaS部署和独立部署,像这次删库事件中的微盟就是采用的SaaS部署的方式。

可以说SaaS部署方式对于购买微信商城系统的企业有着天然的友好度,因为你什么都不用管,只要选择产品套餐——签约——付款——接收微信商城后台网址、账号、密码,简单的几步,就可以在电脑旁开始微信商城的设置。但在购买软件享受操作上便利的同时,也是有很大的制约风险。

在某些情况下,可能会产生连带安全问题,商城某个店铺出现安全攻击,其他店铺乃至整个平台均会受到影响。同时非技术人员可能会在未获得证书认可的情况下访问SaaS应用程序,而这操作可能会导致访问和保护存储在这些应用程序中的任何数据难以管理。一些SaaS应用程序也可能无法与组织使用的其他软件或工具集成。

此外,SaaS软件供应方可能有权访问某些数据,这可能违反了某些组织的合规性要求或隐私政策。这也是为什么这次微盟遭员工“删库跑路”,所有使用微盟SaaS产品的商家也跟着中招的原因。

至于独立部署,应该说是和SaaS的部署方式刚好相反,企业拥有独立的平台版权和自主性,不会受制于第三方平台的规则和政策等,在系统功能扩展上也会比较方便,因为是自己的平台,拥有自己的独立服务器,所有功能随着发展需要都可以在技术条件允许的情况下进行个性化再开发和更新。

与SaaS部署相比,独立部署方式需要企业自己保管服务器、域名及所有数据,因此可以很好地做好数据备份工作。对备份数据区域做好严格的访问控制权限,防止恶意删除备份数据,本次删库事件很大可能是因为员工直接将主备一起删除了。

另外,针对核心数据服务器我们也要做好事前权限预分配,通过细粒度的权限控制,多重身份认证实现事中访问控制,避免权限分配不合理导致误操作、越权操作带来的运维安全事故。有条件的还可以部署数据库安全网关,通过数据库安全网关阻断对数据库发起的恶意删库、误删库、脱库等高危操作。

最后,对于所有的商业交易来说,稳定性永远压倒一切。

macdown为大家带来一款适用于MacOS的应用加密软件Cisdem AppCrypt Mac,只需设置密码并将应用程序和网站添加到锁定列表中,没有人能够在没有正确密码的情况下访问受保护的应用程序和网站。

Cisdem AppCrypt Mac下载

Cisdem AppCrypt Mac软件功能

在Mac上安全锁定应用程序

在Mac上保护您的Mail,Quicken和其他应用程序是一件巨大的痛苦。但如果你不这样做,这很危险。使用Cisdem AppCrypt锁定Mac上的游戏,Facebook,Twitter等应用程序,以便您和您的孩子可以专注于工作和学习。没有更多的分心!

在Mac上阻止网站以提高生产力

来自facebook,ebay或Gmail的消息中断了吗?只需使用Cisdem AppCrypt即可在Mac上创建理想的工作环境。任何网站都可以添加到您的黑名单或白名单中加入此网站拦截器Mac。

AppCrypt家庭

由父母决定孩子可以访问的内容和时间。在关键时刻保持专注的最简单方法。

AppCrypt团队

一种生产力工具,可以在工作时消除所有无意的干扰,并保护应用程序中的私人数据。

所有应用和网站的一个密码

拖放应用程序或添加网站黑名单/白名单并为所有人设置密码。下次,如果没有密码,绝对没有应用和网站!

自定义加密时间表

锁定应用并在特定日期和时间阻止游戏。例如,将其设置为放学后(3:00-5:00),但不要在晚上10:00之后设置。

自动退出非活动应用

当Mac进入休眠状态时,Mac应用程序拦截器将智能地终止当前未在最前面运行的程序。

跟踪失败尝试锁定应用程序

使用日期,时间和可选捕获的照片跟踪失败的尝试,以便您可以查看和查找可疑活动。

隐形的眼睛

使用Mac菜单栏中的图标,Mac应用程序拦截器可用作后台进程,并且在空闲时不会占用CPU负载。

全局快捷方式设置

您可以灵活地设置全局快捷键以快速隐藏/显示主窗口,并在必要时启用/禁用网站拦截器。

.游戏名:飞机大战 X

游戏截图:

游戏开发步骤

1. 准备开发软件,素材

2. 创建新项目,导入素材

3. 编写Event sheet,调试

4. 导出项目,并发布

一,使用Constrcut 2来进行本游戏的开发,所有素材均使用网上素材。

二,新建项目,导入素材

打开Constrcut 2,新建项目

修改你想要的屏幕的大小数据

修改其中window size中的数值

添加素材并布局

双击空白屏幕 添加自己想要的素材类型,将所有素材都添加进来并改名

!

三 、编写Event sheet,调试

打开Event sheet表,添加1-3事件,通过触控控制飞机的方向移动,并在按住飞机时发射子弹对象,松开飞机时停止移动并停止发射子弹。调整子弹发射角度。

添加5-6事件,让子弹在击中的敌机在播放完爆炸动画后销毁,触碰检测为可穿透,子弹也一同消失,并在text中记录分数

添加7-8事件作为事件6的子事件,实现当分数达到一定值后触发出现S和Text3的事件,事件9则是系统触发每0.3秒随机X轴刷新一架敌机的事件

注意设置时注意X轴和Y轴的数据

添加10事件实现当玩家触碰到S时,玩家控制的飞机变成plane的另一动画ーーsuperplane,并在4事件中实现飞机发射的子弹变为三列不同角度

添加11-12事件实现点击右上角的Bomb,出发全体敌军销毁的事件,并添加玩家的飞机碰到敌军时销毁并显示text2的事

至此飞机大战游戏基本完成,玩家通过触控控制自己的飞机上下左右移动并发射子弹,在游戏开始时上方随机刷新出敌机,子弹击中一架敌机就会使其爆炸并加分,当分数达到5时,地图上随机出现强化道具S,玩家碰到S之后便可以强化自己的飞机发射三行子弹。但分数达到100时,跳出胜利的文字、当玩家碰到敌机后则失败

四 、导出项目,并发布

点击右上角的export project来到处项目,选择html5或者安卓项目导出

先说个人发展前,咱们先说说什么是性格,在说说什么是性格测试,在谈谈性格测试和个人发展之间的联系。每个人都有自己的性格,各有不同,但也并非完全不一样,我们可以简单理解成善良、勇敢、胆小等等方面,只是这些元素在人的性格方面程度不懂,导致了性格迥异。如果让你总结你的性格特点,你会怎么总结呢?勇敢机智?温柔贤惠?落落大方?善解人意还是个性偏激?也许你自己都不清楚,但是从小性格就影响着我们的生活和工作,小到班级竞选,大到选择专业。

我不想说什么性格概念和各种套话,我只想让你闭上眼,好好回忆一下你的关键时刻,比如高考、结婚、成长中遇到困难时你的状态。那个时候你一种什么状态,什么样的应激反应,处理方式又是如何?是性格使然随便处理,还是深谋远虑沉着冷静?然后不管如何你都已经做出了选择,你有没有问过自己这样一个问题,“如果我当时不这样,换一个方式,或者换一个态度,再或者等等”,我相信你也这样想过。

这些行为就是性格导致的,简单的说,你是什么性格就会做出什么反应,这就是性格与行为直接的联系。我们总是想往好的方向发展,但是因为当是的冲动,导致向坏的方向发展,很多小伙伴都跟我抱怨,当时面试的时候我要注意某某细节,或者说话方式更肯定一些,那该多好。

这也就是说你不了解你的性格,小了说你控制不住自己的情绪,大了说你不清楚自己的个人规划。一些面试官很喜欢问一个问题就是你的职业生涯规划是什么?我想如果在回答这个问题的时候,把你对性格和工作相融合,你一定能回答出老板想要的答案。针对个人发展和性格我有以下几点要说。

1、

充分了解自己的个性,深刻明确自己适合做什么?

2、

尝试去做各种性格测试,例如MBTI、九型人格、大五人格等专业的性格测试,以丰富自己的见识。

3、

针对性格测试给出的评价要重视,达到扬长避短的目的。

4、

性格测试有多种,感兴趣可以多尝试,毕竟只有庞大的数据量可以让你的测试更加准确。

九型人格主要更偏向于了解个人行为素质,自我认知和个人修养。更适合管理者分析员工、招聘使用。如果你是求职者,我认为可以先参考MBTI,从4个维度中先了解自己的基础性格然后再去了解九型人格,这样对性格测试会更全面一些。

MBTI职业性格测试

九型人格测试

大五人格测试

本文创意来自一次业务需求,这次需要接入一个第三方外部服务。由于这个服务只提供异步 API,为了不影响现有系统同步处理的方式,接入该外部服务时,应用对外屏蔽这种差异,内部实现异步请求同步。

全文摘要:

异步给现有架构带来的问题

Dubbo 异步转同步解决方法

异步转同步架构设计方案

0x00. 前言

现有一个系统,整体架构如下所示:

这是一个很常见的同步设计方案,上游系统需要等待下游系统接口返回调用结果。

现在需要接入另外一个第三方服务 B,该服务与服务 A 最大区别在于,这是一个异步 。调用之后,仅仅返回受理成功,处理结果后续通过异步通知返回。

接入之后,整体架构如下所示:

由于网络隔离策略,通知接收程序与通信服务需要单独分开部署。若没此要求,可以将通信服务 B 与通知接收程序合并成一个应用。

另外图中所有应用采用双节点部署。

为了不影响 上游系统同步处理逻辑,通信服务 B 调用第三方服务之后,不能立刻返回,需要等待结果通知,拿到具体返回结果。这就需要通信服务 B 内部将异步转为同步。

这就是一个典型的异步转同步问题,整个过程涉及两个问题。

通信服务 B 业务线程如何进入等待状态?又如何唤醒正确等待线程?

由于通信服务 B 双节点部署,通知接收程序如何将结果转发到正在等待处理的节点?

问题 1 的解决方案参考了 Dubbo 设计思路。

我们在使用 Dubbo 调用远程服务时,默认情况下,这是一种阻塞式调用方式,即 Consumer 端代码一直阻塞等待,直到 Provider 端返回为止。

由于 Dubbo 底层基于 发送网络请求,这其是一个异步的过程。为了让业务线程能同步等待,这个过程就需要将异步转为同步。

0x01. Dubbo 异步转同步解决办法

1.1 业务线程同步阻塞

Dubbo 发起远程调用代码位于 :

Dubbo 版本为:2.6.X 版本。2,7.X 重构 ,但是本质原理还是一样。

默认情况下,Dubbo 支持同步调用方式,这里将会创建 对象。

这里有个非常重要逻辑,每个请求生成一个唯一 ID,然后将 与 映射关系,存入 中。

这个请求 ID 在之所以这么重要,是因为消费者并发调用服务发送请求,同时将会有多个业务线程进入阻塞。当收到响应之后,我们需要唤醒正确的等待线程,并将处理结果返回。

通过 ID 这个唯一映射的关系,很自然可以找到其对应 ,唤醒其对应的业务线程。

业务线程调用 方法进入阻塞。这段代码比较简单,通过调用 阻塞线层。

1.2 唤醒业务线程

当消费者接收到服务提供者的返回结果,将会调用 方法。

通过响应对象中的唯一 ID,找到其对应 对象,从而将结果设置 对象中,然后唤醒的相应的业务线程。

这里实际有个优化点,使用 done#signalAll 代替 done#signal。使用 condition 等待通知机制的时候需要注意这一点。

详情参考:

1.3 设计注意点

正常情况下,当消费者接收到响应之后,将会从 这个 移除 。

但是在异常情况下,服务提供者若处理缓慢,不能及时返回响应结果,消费者业务线程将会因为超时苏醒。这种情况下 积压了无效 对象。如果不及时清理,极端情况下,将会发生 OOM 。

内部将会开启一个异步线程,定时轮询 判断 超时时间,及时清理已经无效(超时)的 。

0x02. 转发方案设计

根据 Dubbo 解决思路,问题 1 解决办法就比较简单了。具体流程如下:

通信服务 B 内部生成一个唯一请求 ID ,发给第三方服务

若请求成功,内部版使用 存储对应关系,并使业务线程阻塞等待

通信服务 B 收到异步通知结果,通过 ID 查找对应业务线程,唤醒的相应的线程

这个设计过程需要注意设置合理的超时时间,这个超时时间需要考虑远程服务调用耗时,可以参考如下公式:

这里就不贴出具体的代码,详细代码参考 Dubbo 。

接下来重点看下通知服务如何将结果转发给正确的通信服务 B 的节点。这里想到两种方案:

SocketServer 方案

MQ 方案

2.1 SocketServer

通信服务 B 使用 SocketServer 构建一个服务接收程序,当通知接收程序收到第三方服务 B 通知时,通过 将结果转发给通信服务 B。

整个系统架构如下所示:

由于生产服务双节点部署,通知接收程序就不能写死转发地址。这里我们将请求 ID 与通信服务 B 服务地址关系存入 中,然后通知接收程序通过 ID 找到正确的地址。

这个方案说实话有点复杂。

第一 SocketServer 编码难度较大,编写一个高效 SocketServer 就比较难,一不小心可能产生各种 Bug。

第二通信服务 B 服务地址配置在配置文件中,由于两个节点地址不同,这就导致同一应用存在不同配置。这对于后面维护就很不友好。

第三额外引入 依赖,系统复杂度变高。

2.2 MQ 方案

相对 方案, 方案相对简单,这里采用 广播消费的方式,架构如图所示:

通知接收程序收到异步通知之后,直接将结果发送到 。

通信服务 B 开启广播消费模式,拉取 消息。

通信服务 B_1 拉取消息,通过请求 ID 映射关系,没找到内部等待的线程,知道这不是自己的等待消息,于是 B_1 直接丢弃即可。

通信服务 B_2 拉取消息,通过请求 ID 映射关系,顺利找到正在等待的线程,然后可以唤醒等待线程,返回最后的结果。

对比 方案, 方案整体流程比较简单,编程难度低,也没用存在特殊的配置。

不过这个方案十分依赖 消息实时性,若 消息投递延迟很高,这就会导致通信服务 B 业务线程超时苏醒,业务异常返回。

这里我们选择使用 ,长轮询 方式,可保证消息非常实时,

综上,这里采用 的方案。

0x03. 总结

异步转同步我们需要解决同步阻塞,以及如何唤醒的问题。

阻塞/唤醒可以分别使用 。不过这个过程我们需要生成一个唯一请求 ID,并且保存这个 ID 与业务线程映射关系。后续等到结果返回我们才能通过唯一 ID 唤醒正确等待线程。

只要了解上面几点,异步转同步的问题就就可以迎刃而解。

另外,如果你也有碰到异步转同步问题,本文的方案希望对你有帮助。如果你有其他设计方案,欢迎留言,一起讨论~

参考资料

最会说一句 (求关注)

这篇文章其实写了挺久的,写的挺难得。之前很早想到写这篇文章,但是没想好到底咋写,艰难产出。

看到这里,点个关注呀,点个赞呗。别下次一定啊,大哥。写文章很辛苦的,需要来点正反馈。

才疏学浅,难免会有纰漏,如果你发现了错误的地方,还请你留言给我指出来,我对其加以修改。

感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注

欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:studyidea.cn

简介

前面使用spring security默认的认证流程,处理了自定义的用户数据,密码加密,但是在实际开发中,肯定是要使用自己开发的页面、登录成功失败的业务处理等。

关于个性化配置全部在spring-security-browser项目的BrowserSecurityConfig完成。

自定义登录页面

创建登录页面

注意这里的路径:;路径是自定义的,

而UsernamePasswordAuthenticationFilter默认是处理路径的登录请求。下面配置解决这个问题

在BrowserSecurityConfig类重写configure方法

启动项目测试:

处理不同类型的请求

我们做的是一个可重复使用的框架登录页面可能是多个,现在解决这个问题

在spring-security-browser项目创建自定义Controller

在spring-security-browser项目创建返回值类

解决自定义跳转的登录页面,读取yml

修改spring-security-demo项目application.yml

关于系统配置的封装,SecurityProperties最外层封装包含BrowserProperties(浏览器相关系统配置),ValidateCodeProperties(验证码相关系统配置),OAuth2Properties(权限相关系统配置);

在spring-security-core项目新建SecurityProperties类:

同目录BrowserProperties类

要想让上面的配置生效还需要加一个SecurityCoreConfig类

在core项目新建:

改造BrowserSecurityController读取系统配置

现在demo项目系统配置登录页面为:demoLogin.html

demo项目新建demoLogin.html页面:

改造BrowserSecurityConfig类不拦截登录页

启动项目访问:

访问:

注释掉系统配置:

重启项目访问:

今天在工程下新建了一个包,包名为static,结果在里面新建java文件的时候,只能选择kotlin File/classes,没有Java class。如下图

新建后的文件如下:

不是java文件,但是在功能原来其他的包下建没有问题。why?百度一下哈。结果明白了: 文件是java语法的关键字,比如说new return 之类的,这一类文件夹idea是不允许创建的jav文件所以没有java的创建选项。

找了下java 关键字:

static是java关键字,所以无法创建,解决方式当然就是改个包名就行了。

1.引子

1.1.为什么要学习数据结构与算法?

有人说,数据结构与算法,计算机网络,与操作系统都一样,脱离日常开发,除了面试这辈子可能都用不到呀!

有人说,我是做业务开发的,只要熟练API,熟练框架,熟练各种中间件,写的代码不也能“飞”起来吗?

于是问题来了:为什么还要学习数据结构与算法呢?

1.2.如何系统化学习数据结构与算法?

我想好了,还是需要学习数据结构与算法。但是我有两个困惑:

1.如何着手学习呢?

2.有哪些内容要学习呢?

学习方法推荐:

学习内容推荐:

数据结构与算法内容比较多,我们本着实用原则,学习经典的、常用的数据结构、与常用算法

2.考考你

你还记得在数组那一篇中,我们说过基于线性表的数据结构有哪些吗?它们是:数组、链表、栈、队列。

上一篇【数据结构与算法系列六(栈)】中,我们已经详细了解了栈这种数据结构:栈是一种操作受限的数据结构。队列是基于线性表的数据结构中,最后一种了,很巧!它也是一种操作受限的数据结构。

队列同样可以基于数组实现:顺序队列;也可以基于链表实现:链式队列。

那么问题来了:具体如何实现一个队列呢?它都有哪些应用场景呢?

3.案例

3.1.队列的定义

队列是一种基于线性表的数据结构,与栈一样,都是操作受限的数据结构。栈的特点是后进先出,而队列的特点是先进先出(FIFO),就像我们平常在火车站排队候车一样。

队列有两头:队头,和队尾。从队头出队元素,在队尾入队新的元素。

3.2.代码实现

顺序队列代码:

测试代码:

测试结果:

3.3.循环队列代码实现

4.讨论分享

JUC创建线程池:

我想使用Oracle SQL Developer为我的数据库表生成一个ER图,但我是Oracle和这个工具的新手。

在SQL Developer中创建ER图的过程是什么?

#1楼

从SQL Developer 3开始,它非常简单(它们可以使它变得更容易)。

转到?查看→数据建模器→浏览器? 。 浏览器将显示为左侧的一个选项卡。

单击?Browser?选项卡,展开设计(可能称为 ),右键单击?Relational Models?并选择?New Relational Model? 。

右键单击新创建的关系模型(可能是 )并选择?Show? 。

然后将所需的表格(例如“连接”选项卡)拖到模型上。 请注意,当您单击Connections选项卡中的第一个表时,SQLDeveloper会在右侧打开该表:从左侧选择所有表,然后确保在拖动它们之前, 选项卡(或任何名称)是rhs中的活动表结束,因为它可能已切换到您在lhs中单击的其中一个表。

#2楼

为现有数据库模式或其子集创建一个图表,如下所示:

单击文件→数据建模器→导入→数据字典 。

选择数据库连接(如果没有,则添加一个)。

单击下一步 。

检查一个或多个模式名称。

单击下一步 。

检查要导入的一个或多个对象。

单击下一步 。

单击完成 。

显示ERD。

导出图表如下:

单击文件→数据建模器→打印图表→到图像文件 。

浏览并选择导出文件位置。

单击保存 。

该图表已导出。 要以矢量格式导出,请使用“转换为PDF文件” 。 这允许使用Inkscape (或其他矢量图像编辑器)进行简化编辑。

这些指令可能适用于SQL Developer 3.2.09.23到4.1.3.20。

#3楼

它很容易去文件 - 数据建模器 - 导入 - 数据字典 - 数据库连接 - 好的

#4楼

在Oracle SQL Developer中由Jeff Smith ( 链接 )描述了在Oracle SQL Developer中生成实体关系图的过程。

摘抄:

实体关系图

入门

要完成该示例,您需要一个Oracle数据库实例,其中包含默认数据库安装中可用的示例HR模式。 您还需要Oracle SQL Developer 4.0版,您可以通过Data Modeler子菜单访问Oracle SQL Developer Data Modeler [...]或者,您可以使用独立的Oracle SQL Developer Data Modeler。 两种实现中的建模功能完全相同,都可以从Oracle Technology Network免费下载。

在Oracle SQL Developer中,选择View - > Data Modeler - > Browser。 在“浏览器”面板中,选择“关系模型”节点,单击鼠标右键,然后选择“新建关系模型”以打开空白模型图面板。 您现在与使用独立Oracle SQL Developer Data Modeler的人在同一个地方开始。 导入数据字典

导入数据字典

Oracle SQL Developer Data Modeler中的设计由一个逻辑模型和一个或多个关系和物理模型组成。 要开始创建设计的过程,必须从现有数据库导入架构信息。 选择文件 - >数据建模器 - >导入 - >数据字典以打开数据字典导入向导。

单击“添加”以打开“新建” - >“选择数据库连接”对话框,并以HR用户身份进行连接。 (有关从Oracle SQL Developer创建连接的详细信息,请参阅2008年5月/ 6月的Oracle Magazine中的“建立数据库连接”。)

选择连接,然后单击“下一步”。 您将看到可以从中导入的模式列表。 在“过滤器”框中键入HR以缩小选择列表的范围。 选中HR旁边的复选框,然后单击“下一步”。

阅读更多...

#5楼

对于使用oracle db的类图,请使用以下步骤

文件→数据建模器→导入→数据字典→选择数据库连接→下一步→选择数据库 - >选择表格 - >完成

前一篇文章“linux入门系列15--文件传输之vsftp服务”讲解了文件传输,本篇继续讲解文件共享相关知识。

文件共享在生活和工作中非常常见,比如同一团队中不同成员需要共同维护同一个文档,在windows环境下,通常会选用第三方协作工具,如腾讯文档,石墨文档等等。

之前讲解了基于ftp的文件传输,为何还会单独讲解文件共享呢?试想一下,假如我们要修改服务器上某个文件,如果使用ftp的话,需要先下载下来进行修改,然后在上传到服务器。这样是很繁琐的,这时候就可以使用文件共享来解决这个问题。

文件传输和文件共享有本质的区别,基于ftp协议的文件传输可以实现不同机器之间文件的传输和拷贝,会产生多个副本。而文件共享则只有一个副本,各个客户端连接到共享服务器操作的是同一份文件。

Linux环境下可以通过Samba服务或NFS服务来实现文件共享,下面分别进行介绍。

一、文件共享服务Samba

1.1 Samba概述

为了解决局域网内的文件和打印机等资源的共享问题,微软和英特尔与1987年共同制定了 SMB(Server Messages Block,服务器消息块)协议,这使得多个主机之间共享文件变得简单。

到了1991年,一个国外牛逼大学生 为了解决 Linux 系统 与 Windows 系统之间的文件共享问题,基于SMB协议开发出了SMBServer服务程序。它是一款开源的文件共享软件、只需要简单的配置就能实现文件共享。

后来作者把它注册商标为Samba,它**现在已经成为Linux系统和Windows系统之间共享文件的最佳选择**。

1.2 Samba安装及配置文件

安装之前规划并准备实验环境

角色

操作系统

ip地址

主机名称

Samba服务器

Centos7

192.168.78.101

sambaserver

Samba客户端

Centos7

192.168.78.102

cliet

Samba客户端

Windows10

宿主机即可

Samba服务程序的名字就是软件包的名字,安装后的服务名叫smb,在sambaserver主机上,通过rpm名称查看系统是否已经安装,如果没有安装则通过Yum软件仓库进行安装。

安装完成成后,配置文件所在目录为:/etc/samba/,主配置文件为:smb.conf

接下来我们看下配置文件里的主要内容,还是按“linux入门系列15--文件传输之vsftp服务”中的2.4.1提到的老规矩,将注释以及空行信息去掉,便于观察配置项。

以上脚本通过grep命令-v参数反向选择,去掉以#和分号开头的注释信息,用^$过滤掉空白行,过滤无用信息后通过重定向覆写到原始配置文件中。

接下来看看主配置文件的内容:

我直接把注释添加到每项配置的后边,方便查看。

其中[homes]参数为来访用户的家目录共享信息,[printers]参数为共享的打 印机设备,如果不需要可以手动直接删除即可。

1.3 Samba文件共享实操

通过前文可以看到,Samba服务程序的主配置文件包括全局配置参数和区域配置参数。顾名思义,全局配置参数用于设置整体的资源共享环境,对每一个独立的共享资源都有效;而区域配置参数则用于设置单独的共享资源,且只对该资源有效。

下面我们就来配置并使用Samba服务

1.3.1 文件共享服务器配置

(1)Samba主配置文件配置

添加以上内容并保存退出。

补充一下主配置文件中全局配置global的2个参数,security和passdb backend 。

从1.2中讲到的主配置文件中可以看出,它们的默认值分别为user和tdbsam。

其中security有4种安装认证方式,分别如下:

share:来访主机无需验证口令,比较方便,但安全性很差

user:需验证来访主机提供的口令后才可以访问,提升了安全性

server:使用独立的远程主机验证来访主机提供的口令(集中管理账户)

domain:使用域控制器进行身份验证

passdb backend有3种方式,定义用户后台的类型:

smbpasswd:使用 smbpasswd 命令为系统用户设置 Samba 服务程序的密码

tdbsam:创建数据库文件并使用 pdbedit 命令建立 Samba 服务程序的用户

ldapsam:基于 LDAP 服务进行账户验证

接下来我们就采用默认的user验证模式来进行设置,其他模式的搭配使用暂时不做讲解。

(2)创建访问共享资源的账号

从默认配置文件中可以看出,在RHEL7中Samba采用默认的user认证模式,可以确保仅让有密码且受信任的用户访问共享资源,需要先建立账户信息数据库,并且要求账户必须在当前系统中已经存在。

与前文讲解ftp时类似,要求用户在当前系统中已经存在,是为了避免在创建文件时引起文件权限属性混乱而引发错误。

用于管理 SMB 服务程序的账户信息数据库的命令为:pdbedit。

语法格式:

pdbedit [参数] 账户

参数:

参数

作用

-a

建立Samba用户

-x

删除Samba用户

-L

列出账户列表

-Lv

列出账户详细信息的列表

先创建用户samba,并设置密码为:123456

使用功能pdbedit创建samba服务的用户,密码为888888(注意此处的密码不是samba系统用户登录的密码,虽然也可以设置密码给之前密码相同,但一定要分清楚这2个密码是单独的,不要弄混淆)

(3)创建共享资源的目录

在前面的第一步中我们配置了共享目录为/home/database。因此我们来创建此目录,需要考虑到文件读写权限问题。

(4)SELinux上下文和策略设置

由于/home目录是系统中普通用户的家目录,因此不仅要考虑文件读写权限,还要注意SELinux安全上下文以及域策略所带来的限制(原始的主配置文件中有关于SELinux安全上下文策略的配置说明)。

设置SELinux安全上下文

除了设置上下文,还需要设置SELinux域相关策略,开启相关的策略即可

(5)防火墙设置

samba服务名称为smb,重启并设为开机启动,清空防火墙策略

至此,就可以用客户端使用samba服务实现文件共享了

先在共享目录准备一个文件用于客户端共享

这样客户端就可以查看并编辑此共享文件了。

Samba支持Windows、Linux、macOS之间文件共享,接下来演示客户端是Linux和Windows的情况。

1.3.2 Linux客户端访问文件共享服务

在之前准备的samba客户端主机上安装客户端后,用之前的samba账号密码就可以共享文件了。

(1)安装共享客户端

(2)创建认证文件

保存并退出,用户密码为之前1.3.1第二步中创建的samba用户及对应的密码,domain为主配置文件中的域。

为了保证不被其他人随意看到,最后把这个认证文件的权限修改为仅root管理才能够读写。

(3)本地创建目录挂载Samba共享目录

这样就可以查看并修改共享服务器内的内容

1.3.3 Windows访问文件共享服务

本演示以win10为例

在开始菜单输入共享服务器地址

在弹出弹出登录框输入正确的账号密码

进入共享目录

进入目录后,就可以执行查看、写入、更名、删除文件等操作,在samba服务器和对应的其他客户端就可以看到文件的变化。因此就实现了文件共享。

二、网络文件系统NFS

如果你只是在Linux主机之间共享文件,那NFS将更加简单。

NFS(Network File System即网络文件系统)服务可以将远程 Linux 系统上的文件共享资源挂载到本地主机的目录上,从而使得客户端基于TCP/IP协议,像使用本地主机上的资源一样读写远程Linux系统上的共享文件。

虚拟机准备,可以单独新克隆两台虚拟机,此处我就直接用原来的2台机器,只是要注意为了避免上一实验的干扰,将之前的Samba客户端作为nfs的服务器,将Samba服务器当作nfs的客户端。规划如下:

角色

操作系统

ip地址

主机名称

NFS客户端

Centos7

192.168.78.101

sambaserver

NFS服务器

Centos7

192.168.78.102

cliet

2.1 NFS服务器配置

(1)安装NFS

RHEL7系统中默认已经安装了NFS服务,可以通过rpm命令查看,当然也可以直接执行如下安装命令,如果有新的包会自动更新,如果已是最新版本则提示Nothing to do。

NFS包名为nfs-utils。

(2)在NFS服务器创建共享目录

创建目录,修改权限,并创建共享文件。

(3)NFS服务配置

NFS服务程序配置文件为/etc/exports,默认为空。采用如下格式定义要共享的目录和相应的权限。

格式:共享目录的路径 允许访问的 NFS 客户端(共享权限参数)

相关参数及作用如下

参数

作用

ro

只读

rw

读写

root_squash

当NFS客户端以root管理员访问时,映射为NFS服务器的匿名用户

no_root_squash

当 NFS客户端以root管理员访问时,映射为NFS服务器的root管理员

all_squash

无论NFS客户端使用什么账户访问,均映射为NFS服务器的匿名用户

sync

同时将数据写入到内存与硬盘中,保证不丢失数据

async

优先将数据保存到内存,然后再写入硬盘;这样效率更高,但可能会 丢失数据

按上边的语法格式,把第一步创建的nfs目录共享给192.168.78.0/24网段内的所有主机,让这些主机都有读写权限。为了最大限度保证数据不丢失,在将数据写入到NFS服务器的硬盘中后才会结束操作,同时把来访客户端root管理员映射为本地的匿名用户,配置如下:

保存并退出,注意ip地址与权限之间没有空格。

(4)设置防火墙策略

直接清空iptables防火墙的默认策略,以免默认的防火墙策 略禁止正常的NFS共享服务。

(5)启动NFS服务程序

在使用NFS服务进行文件共享之前,需要使用RPC(Remote Procedure Call,远程过程调用)服务将NFS服务器的I 地址和端口号等信息发送给客户端。

因此,在启动NFS服务之前,还需要重启并启用 rpcbind 服务程序,并将这两个服务一并加入开机启动项中。

至此NFS服务器端配置完成,接下来配置NFS客户端。

2.2 NFS客户端使用

(1)查看NFS服务器

查看NFS服务器远程共享信息使用showmount命令。

语法:

showmount [参数] 服务器ip

参数:

参数

作用

-e

显示NFS服务器的共享列表

-a

显示本机挂载的文件资源的情况

-v

显示版本号

输出格式为:“共享的目录名称 允许使用客户端地址”

(2)创建目录并挂载

使用mount命令并结合-t 参数,指定要挂载的文件系统的类型为nfs,并在命令后面写上服务器的 IP地址、服务器上的共享目录以及要挂载到本地系统的目录/nfs,这样就可以看到共享的目录文件了。

(3)挂载信息持久化

为了保证上一步骤的挂载能一直生效,需要将其写入到fstab文件中。

这样就在本地完成了服务器的文件共享。

三、自动挂载服务autofs

3.1 autofs概述

无论是之前讲解本地yum源的安装还是本文讲解的Samba和NFS服务,都需要将挂载信息接入到/etc/fstab文件中,这样才会使共享资源自动随服务器开机而进行挂载。

但是如果挂载的远程资源太多,则会给网络带宽和硬件资源带来很大的压力。如果挂载后长期不使用,会造成资源的浪费。

为了不造成浪费,可以在每次需要使用才才去手动挂载,但是这样操作又非常繁琐。为了解决这个问题,autofs自动挂载服务应运而生。

与mount命令不同,autofs服务程序是一 种Linux系统守护进程,当检测到用户视图访问一个尚未挂载的文件系统时,将自动挂载该文件系统。

简单说就是,将挂载信息写入/etc/fstab文件后,系统在每次开机时都自动将其挂载,而autofs 服务程序则是在用户需要使用该文件系统时才去动态挂载,从而节约了网络资源和服务器的硬件资源。

3.2 autofs安装及配置文件

3.2.1 autofs安装

3.2.2 配置文件解释

autofs服务程序主配置文件为:/etc/auto.master,我们一般采用主配置和子配置的方式进行配置。原因是生产环境中,可能会同时管理很多设备的挂载操作,如果把所有挂载信息都写入主配置文件,一旦内容多了将难以管理,且不利于服务执行效率。

主配置文件中采用“挂载目录 子配置文件”的格式填写,挂载目录是设备挂载位置的上一级目录。例如,光盘设备一般挂载到/media/cdrom目录中,那么主配置文件中的挂载目录就写成/media即可。

子配置文件需要用户自定义,文件名称没有严格要求,后缀建议以.misc结束。格式为:“挂载目录 挂载文件类型及权限 :设备名称”。

下边就以自动挂载光驱为例,进行演示autofs的用法

3.3 以光驱为例演示sutofs使用

3.3.1 autofs配置

主配置文件

添加/media /etc/iso.misc保存并退出,其他内容不变。

子配置文件

保存并退出。

说明:把光盘设备挂载到/media/iso 目录中,子配置文件可将挂载目录写为 iso,而-fstype 为文件系统格式参数,iso9660 为光盘设备格式,ro、nosuid 及 nodev 为光盘设备具体的权限参数,/dev/cdrom 则是定义要挂载的设备名称。

3.3.2 启动autofs服务

配置完成后,启动autofs服务并加入开机启动

3.3.3 使用autofs服务

经过以上操作我们配置并开启了autofs服务,我们先来看下是否已经给我们挂载好了光盘设备

可以看到光标设备没有挂载上,并且/media 目录中根本就没有 iso 子目录

接下来,我们直接进入到挂载的/media/iso目录,看看是否有内容

以上演示说明,当切换到iso目录时,也就是说只有使用挂载资源时,autofs才自动进行挂载。当系统重启后可以看到它没有挂载上去,而再次切换到/media/iso目录时,又会自动挂载。通过这种方式实现了按需分配,从而节约带宽等资源。

在讲解完文件共享之后,下一篇文章将分享使用Postfix和Dovecot搭建邮件系统。

百度有一个基木鱼免费智能建站系统,主要给广告主用的。

有些客户投放的广告统计数据,发现自己的网站来自百度的很少,反正赠送的基木鱼网站流量多了,原来是主动给把客户的落地页切换到了基木鱼页面,尤其是做传统行业的网站表现的更为严重。

现在在一些微信群内用户怨声四起。其理由就是在不同客户的情况下,把广告主的网站主动切换到基木鱼平台。

当然,可能是投放广告时,隐藏按钮太多,客户不稍不注意就选择了“切换网站到基木鱼”。

在这些年的SEO诊断中,我这里发现不低于20%的网站都存在着一个严重的抓取问题,但是奇怪的是,却始终得不到大多数人的重视。而研究透彻并分享这个问题的人,更是没有发现。今天这篇文章,我们就来看一下这个问题。

连接超时,或者抓取超时,如果你的网站出现过这个问题,至今没有处理到位的,就把这篇文章转载一下吧。

很多人说,不就是几十次的超时吗?有什么大不了的,如果这样认为,那么真的是活该网站做不好了,正常的网站是什么样的呢?我们来举个例子:

这是一个权重4的站点,哪怕十多万次的抓取,却仍旧没有出现过一次的抓取错误!如果你的每日抓取错误量超过10次(十天半个月就出现一次错误,或者连续多天出现的),就真的应该严肃处理一下这个问题了,因为当前问题的存在,已经长期的制约了网站的发展上限,甚至因为这个问题,诱发网站的降权,都一点不奇怪。

有人说,不用理会,这就是一个BUG,因为我在网站的LOG日志中,并没有发现这个问题。之前在百度站长的VIP群里,有人这样解释过。但是我想说的是:百度无法抓取到网站,你的网站LOG日志,能统计到这样的错误吗?所以,上述解释是完全说不过去的。

A,DNS问题,无论是蜘蛛还是用户访问网站,首先就需要域名的DNS做了正确的解析,然而解析是解析了,你能确定DNS真的稳定吗?不少站点的错误,正是因为DNS不稳定而造成的。尤其是那些又做域名注册,又搞‘域名抢注’的平台,这样的平台,更容易出现DNS解析不稳定的情况,尤其是在抢注高峰期。具体哪些品牌,这个就不点名了,如果你的域名在这些平台中,贺贵江强烈建议你将其转出。

B,页面太大,有一些网站的部分页面达到了四五六七八兆的情况,甚至还见过极少的网站出现过一个页面有10MB大小。这样的页面,更容易出现链接超时。正常情况,并不建议网站的页面大小超过3MB,同时建议网站服务器启用GZIP压缩。

C,带宽不足,这是一个很常见的情况,不少站点出现链接超时都是因为带宽不足而引发的。我们知道1M的带宽峰值下载是128KB,如果网站是1M接入,页面大小是256KB,如果同时2个人在打开页面,那么就需要4秒才可以下载完毕。如果带宽是2MB,页面大小是1024KB,一个人打开网站需要8秒,可如果是10个人同时打开呢?所以带宽这里,一定要做足。站长或者企业都应当观察一下站点的平均带宽,以及峰值带宽情况,如果经常出现‘带宽峰值’类型的访问,那么就一定要升级带宽了。

D,首字节时间,可能很多同学没有听说过这个问题,它指的是从发送请求到WEB服务器的时间+WEB服务器处理请求并生成响应花费的时间。有一些站点,首字节时间居然都可以达到5MS,这样的情况,网站还能正常吗?

1、对于首字节时间这里,我们应当注意:避免网站与其他网站共享同一个服务器其他网站会占用自身网站服务器处理时间。

2、CDN减少内容和访问者之间的“距离”将静态内容分发到CDN,CDN的内容自动复制到各个位置,在地理上更接近用户,从而减少TTFB的时间。注意的是,已定要设置‘缓存时间’针对于网站访问较慢的页面或栏目。

3、避免网站使用虚拟主机系统如网站在一段时间未有流量产生,系统会推迟或暂停虚拟服务器,当新访客进入时,将重新进行服务器备份处理(10s或更长时间);

4、后端优化软件性能减少服务器生成的响应到浏览器的时间,比如:操作码缓存、服务器端缓存、本地缓存、W3总缓存等。

首先是为网站找一家靠谱的DNS服务商,我们建议是阿里云,其次是稳定的CDN服务商,阿里、百度智能云、腾讯云,都可以。最后就是网站服务器,尽量不要选择使用虚拟主机,且尽量避免多个站点使用相同服务器。

字节跳动现已推出“头条搜索”独立App,目前,“头条搜索”App已经在各大安卓应用商店低调上线。头条搜索App可搜到今日头条站外内容。

“头条搜索”App的整个产品框架,与“百度搜索”App的设计与模式相似。

当前,很多企业同时是字节跳动和百度两家的广告代理商,字节有了搜索业务,就能蚕食百度等搜索企业的目标客户。如果业务开展顺利,一年营收增加百亿以上并不是难题。

但头条搜索想单凭此就超越竞争对手,显然还远远不够。在搜索这一赛道里,头条搜索面临的竞争十分激烈:前有作为“一超”的百度,后有紧追不舍的搜狗、阿里的神马搜索、奇虎的 360 搜索等多方“列强”。

扩展阅读:2019年8月,头条搜索网页版上线,地址为但将头条搜索网站的开头网址“m”改为“

Wide Web)后,便只能搜到今日头条站内内容。字节跳动招聘官方2019年早些时候发布的招聘启事显示,其将从0到1打造一个通用全网搜索引擎。

卢松松博客有一台阿里云主机,眼看还有一个月到期了。此时阿里云正好在做活动,我想正好借助这个活动续费能优惠一些。

虽然阿里云对续费主机非常不友好,续费贼贵。但好歹还是送了100元的续费优惠劵,虽然优惠券没腾讯云给力,但好歹也是送了,6000多元,能省100是100。

当我领卷之后,阿里云突然弹出提示,告诉我点此免费升级配置,升级后更安全、配置更强大、更符合当前网站运营环境。我也没仔细看,就点升级了。

等我升级后,发现这台机器的续费竟然涨价了20%。。。。

此时服务器还有1个月到期,之前续费是6K多,而现在要7K多。

对我来说,让享受这一个月所谓的升级配置没任何意义,更坑爹的是服务器还不能降配。就这样莫名其妙的让我续费多花了1K。

感悟:现在的云服务商为了拓展新客户,大大价格站,新用户非常便宜,但对老用户特别坑爹,阿里云现在在中国占据了差不多一半以上的市场份额,马上具备垄断的条件了,一旦垄断,全部完蛋。

虽然我们松松商城也是阿里云的代理商,但阿里云给我们的返点非常少,销售动力不足。我们给客户折上折的力度很小,每单只能折上折几十几百元。

原因:打包后的文件要在服务器中运行

解决方法:放在服务器中打开,依次在终端执行下面命令

另外,本地打开方法:

vue cli2

将config文件夹内index.js文件中 build的assetsPublicPath的’/’改为’

vue cli3

在项目根目录内vue.congfig.js文件(如没有,自行创建)加上如下代码

注意:本地打开,需要把路由的mode: 'history'注释掉

二、表单属性

1、显示名

表单的显示名新增的时候是可以随意修改的,但是保存后就不能再修改了,也不建议去修改,因为会出错。如果要修改如下图所示的名称,有两种方法:

第一是种修改表单的国际化设置,第二种是修改编辑页面设计该表单所处的tab分页(具体的修改方法请看第五章的布局属性)。

2、表名

表示的是存在数据库中的名称。

3、属性名

属性名是唯一的,用于区分各表单的一个属性。一经创建就不能修改。

?SqlKey其实就是模块的属性名+表单属性名+字段名

4、可见

用于控制表单是否可见。

5、只读

目前此属性无效。

6、列数

这个属性只有在新增表单的时候有用,一旦保存后这个属性就无效了。用于新增表单时控制表单的列数用的。

7、列表高度

用于控制列表的高度。

8、显示工具条

用于控制表单的工具条的显隐。这个属性对于主表是无效的,只对子表有用。

子表没有勾选上这个属性,效果如下:

勾选上后,效果如下:

9、主表

该属性勾选上就表示当前表单是主表,每个模块只有一个表单可以勾选上这个属性,要不然会报错。

10、是否虚拟表

该属性被勾选后,就不会在数据库中创建该表,而且这个表单就会同自定义表单一样,也就是说勾选上这个属性就是另类的自定义表单。

11、是否列表显示

该属性表示让表单以列表的形式显示,一般只有自定义表单才会用到这属性,或者是勾选了【是否虚拟表】的表单。

12、列表展示类型

列表展示类型一共有三个值:DataGrid、表单Form和FullCalendar。

普通模块,列表展示类型默认是DataGrid。

自定义表单,列表展示类型默认是表单Form(注只有以列表形式显示的时候才默认是表单Form,要不然还是DataGrid)。

FullCalendar是日历型的列表展示,具体效果具体效果如下图:

此处以会议室预约为例来介绍日历控件的使用。

首先创建一个【会议室预约】模块,再创建好模块需要的字段。

点击表单,在右侧的表单属性处找到列表展示类型,选择FullCalendar值。

创建一个业务字典【会议室】,建好表字段。

保存更新。

添加完会议室的数据后,就可以操作会议室预约模块了。可以通过新增按钮来新增记录,也可以随意点击一个方块来添加内容。

数据添加后的现实效果:

13、启用分页选择

当你勾选上这个属性后,具体效果如下图:

有何作用呢?就是你想选取的数据可能不在同一页,但是当你切换到第二页的时候,当前页勾选的数据就会没了,所以就需要你把当前页的数据勾选上数据下推到上图所示的红框内,在切换到第二页,继续下推数据到红框内,依次类推。(注:此处的下推按钮目前需要自己写,以后会优化成系统自带此按钮)

14、动态显示

此字段只对主表有效果,对子表无效。因为这个字段是用来控制子表的显示的。此属性需要与主表中的一个字段配合使用,才能发挥效果。主表的表单属性勾选上此属性后,在主表的字段中选取一个字段,配置字段属性的动态显示设置这个属性,此属性需要有sql语句与其关联,才能产生效果。具体看如下图:

配好sql后,保存更新,就能在应用平台来查看具体效果了。

该模块一共有四张主表,如下图:

但是应用平台上只显示了一张子表,这就是通过动态显示来控制的。通过更改检查记录模板的值来更改显示的子表。效果可以看如下图:

15、关联实体表名

该属性表示当前表单与另一个表单产生关联,公用同一张数据库表,也就是不会产生新的数据库表,当前表单进行增删改查时,关联的表单也会产生同样的效果。这个属性的具体效果,在移动端模块也有详细的说明,就不多介绍了。

16、字段权限启用

只有勾选了表单属性的字段权限启用,字段的字段权限启用才有效果,如果没有勾选表单的,只勾选子表的,是没有任何效果的。

17、状态作为标签查询

该属性勾选上后会在列表的顶部显示系统状态的标签,可以通过该标签来筛选数据。

18、隐藏系统状态

该属性勾选上后,列表中的状态字段会隐藏掉(可以与上图进行比较)。

####19、显示记录数范围

该属性用来控制列表页的显示记录数,如下图:

具体配置如下图,每个数字之间用逗号隔开:

####20、默认显示记录数

该属性用来配置默认显示的记录数。

21、默认排序字段

设置列表页面的数据默认根据什么字段进行排序,每个值之间用逗号隔开

22、默认排序方式

设置排序的方式(asc(升序),desc(降序)),每个值之间用逗号隔开。注:默认排序字段有几个值,排序方式就有几个与其对应。

23、事件编辑

事件编辑分两种:编辑属性的事件编辑和查询属性的事件编辑。

编辑属性的事件编辑又称为表单事件,多用于主表和编辑页面。

查询属性的事件编辑又称为列表事件,多用于子表和列表页面。

24、自定义查询

这个属性只能用于自定义表单和虚拟表,用于填写sql语句来查询数据。

25、汇总统计

首先点击想要统计的表,然后在右侧的表单属性面板的查询属性中找到汇总统计并点击,会弹出一个弹窗。

点击添加,填写相应的信息。统计列是选择你要统计的字段,显示名称随便填(如果你是统计多个字段的话,这个显示名称必须是统一的,比如说:汇总),统计类别的选择自己想要的,统计规则可不填(统计规则只有在统计类别选择自定义规则时才有用,统计类别中用到最多的还是前面几个,比如:求和、平均值之类的。自定义规则是基本用不上的,所以可以不管这个统计规则。)。

保存更新,汇总统计自动生成。

26、自定义按钮

首先点击想要生成自定义按钮的表单,然后在右侧属性面板的查询属性处找到自定义按钮并点击。

在弹出的弹窗上点击添加,并填写相应的信息。显示名随便填,按钮图片选择自己喜欢的图片,其他的不用管。

按钮创建完成后,就需要进行最重要的一步了,就是添加事件。点击设置,进入事件编辑页面。

在左侧的事件类型中选择onclick事件(其他事件将会在后面的章节做具体的介绍),然后在右侧的函数编辑页面编写js代码。本平台提供了很多自带的函数方法,具体的函数现在不作介绍,后面会另开一个章节做详细的介绍。

保存并更新,自动生成按钮。如果在页面上没有显示按钮,就可能是你没有勾选上显示工具栏。只要在表单属性中找到显示工具栏工具栏勾上就可以了。

使用 ES2015 模块,您可以将应用程序代码分成可重用的、封装的、专注于单一任务的模块。

这很好,但是如何构造模块呢?一个模块应该有多少个函数和类?

这篇文章介绍了有关如何更好地组织 JavaScript 模块的4种优秀实践。

1.优先使用命名导出

当我开始使用 JavaScript 模块时,我使用默认的语法来导出模块定义的单个块,不管是类还是函数。

例如,这是一个将模块 Greeter 导出为默认值的模块程序:

随着时间的推移,我注意到了重构默认导出的类(或函数)的困难。在重命名原始类时,使用者模块中的类名没有改变。

更糟糕的是,编辑器没有提供有关要导入的类名的自动完成建议。

我的结论是,默认的导出并没有带来明显的好处。然后我转向了命名导出。

让我们将 Greeter 命名为出口,然后看看好处:

使用命名导出,编辑器可以更好地进行重命名:每次更改原始类名时,所有使用者模块也会更改类名。

自动完成功能还会建议导入的类:

所以,这是我的建议:

"支持命名模块导出,以受益于重命名重构和代码自动完成功能。"

2.导入期间不进行繁重的计算工作

模块级别范围定义了函数、类、对象和变量。该模块可以导出其中一些组件,就像这样。

模块级范围不应该进行繁重的计算,比如解析 JSON、发出 HTTP 请求、读取本地存储等等。

例如,下面的模块配置解析来自全局变量 bigJsonString 的配置:

这是一个问题,因为bigJsonString的解析是在模块级范围内完成的。bigJsonString的解析实际上是在导入配置模块时发生的:

在更高的级别上,模块级范围的作用是定义模块组件、导入依赖项和导出公共组件:这是依赖项解析过程。它应该与运行时分离:解析JSON、发出请求、处理事件。

让我们重构配置模块来执行延迟解析:

因为 data 属性被定义为一个 getter,所以只有在使用者访问 configuration.data 时才解析bigJsonString。

消费者更清楚什么时候进行大的操作,使用者可能决定在浏览器空闲时执行该操作。或者,使用者可能会导入模块,但是出于某种原因不使用它。

这为更深层的性能优化提供了机会:减少交互时间,很大程度地减少主线程工作。

导入时,模块不应该执行任何繁重的工作。相反,使用者应该决定何时执行运行时操作。

3.尽可能的使用高内聚模块

内聚性描述了模块内部各个组件在一起的程度。

高内聚模块的函数、类或变量是密切相关的,他们专注于单个任务。

formatDate 模块具有很高的内聚性,因为它的功能密切相关,并且侧重于日期格式化:

formatDate()、ensureDateInstance() 和 MONTHS 彼此密切相关。删除 MONTHS 或ensureDateInstance() 会破坏 formatDate():这是高内聚的标志。

4.避免较长的相对路径

我发现很难理解一个模块的路径包含一个,甚至更多的父文件夹:

而有一个父选择器 通常不是问题,拥有 2 个或更多通常很难掌握。

这就是为什么我建议避免使用父文件夹,而使用绝对路径:

尽管有时写入绝对路径的时间更长,但是使用绝对路径可以使导入的模块的位置清晰明了。

为了减少冗长的绝对路径,可以引入新的根目录。例如,这可以使用 babel-plugin-module-resolver实现。

使用绝对路径而不是较长的相对路径。

每天都会有更新看过的朋友可以关注一波,Java学习路线和优质资源评论或点击“java”获取

5.结论

JavaScript 模块非常适合将您的应用程序逻辑拆分为多个独立的小块。

通过使用命名的导出而不是默认的导出,可以在导入命名组件时更轻松地重命名重构和编辑器自动完成帮助。

使用 import {myFunc} from 'myModule' 的唯一目的就是导入 myFunc 组件,仅此而已。myModule的模块级范围应该只定义包含少量内容的类、函数或变量。

一个组件应该有多少个函数或类,这些组件应该如何与每个组件相关联?支持高内聚的模块:它的组件应该紧密相关并执行一个共同的任务。

包含许多父文件夹 的长相对路径很难理解,将它们重构为绝对路径。

什么是云原生

为了解决传统应用升级缓慢、架构臃肿、不能快速迭代、故障不能快速定位、问题无法快速解决等问题,云原生这一概念横空出世。

Pivotal 是云原生应用的提出者,并推出了 Pivotal Cloud Foundry 云原生应用平台和 Spring 开源 Java 开发框架,成为云原生应用架构中先驱者和探路者。

早在 2015 年 Pivotal 公司的 Matt Stine 就写了一本叫做迁移到云原生应用架构的小册子,其中探讨了云原生应用架构的几个主要特征:

符合 12 因素应用

面向微服务架构

自服务敏捷架构

基于 API 的协作

抗脆弱性

后来 Pivotal 对云原生的定义做过几次更新, 最新的 Pivotal 官网上对云原生应用的最新介绍是关注以下四点:

集成 DevOps

持续交付

微服务

容器化

DevOps 是软件开发人员和 IT 运营之间的合作,目标是自动执行软件交付和基础架构更改流程。它创造了一种文化和环境,可在其中快速、频繁且更可靠地构建、测试和发布软件;

持续交付使得单个应用更改在准备就绪后即可发布,而不必等待与其它更改捆绑发布或等待维护窗口期等事件。持续交付让发布行为变得平淡可靠,因此企业可以以更低的风险频繁交付,并更快地获得最终用户的反馈,直到部署成为业务流程和企业竞争力必不可少的组成部分;

微服务是将应用作为小型服务集合进行开发的架构方法,其中每个服务都可实施业务功能,在自己的流程中运行并通过 HTTP API 进行通信。每个微服务都可以独立于应用中的其他服务进行部署、升级、扩展和重新启动,通常作为自动化系统的一部分运行,可以在不影响最终客户的情况下频繁更新正在使用中的应用;

与标准虚拟机相比,容器能同时提供效率和速度。单个操作系统实例使用操作系统 级的虚拟化,在一个或多个隔离容器之间进行动态划分,每个容器都具有唯一的可写文件系统和资源配额。创建和破坏容器的开销较低,再加上单个虚拟机中的高包装密度,使容器成为部署各个微服务的完美计算工具。

Google 主导成立了云原生计算基金会(CNCF),对云原生的定义为:

“云原生(Cloud Native)技技术帮助企业和机构在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、 服务网格、微服务、不可变基础设施和声明式 API;这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术可以使开发者轻松地对系统进行频 繁并可预测的重大变更 。”

目前云原生背后最大的推手就是 CNCF,关键技术包括容器、微服务、服务网格、devops,声明式的 API 等等。

云原生应用与传统应用的对比,云原生应用可以充分利用云的优势,灵活地在各个云厂商分发应用,释放企业生产力,聚焦到业务创新上,而不是花费更多的时间在适配和扩展不同的基础设施平台上。

云原生时代的 DevOps 新挑战

首先我们要清楚地知道, 站在企业的角度来看,在这样一个快捷商业的时代,企业最需要什么?

唯快不破。这里的快可以解读出来两层含义,一是业务应用快速上线,有利于抢占市场先机,第二层意思就是在你的业务有爆炸式增长的时候,你如何在计算资源上给以充分的保证,这个时候其实追加巨额的 IT 投资购买软硬件也未必能跟得上业务的快速发展。这个其实就是企业研发效能的问题;

稳中求变。业务或者应用的稳定性永远都是第一位的,如何既保证业务的“稳态”又要满足快捷商业的“敏态”需求,比如新业务的上线、应用的变更等。这个是企业 IT 架构的问题;

节省资源,如何节省计算资源,根据业务是否高峰自动扩容缩容,这个是云平台建设的问题;

开拓创新,开发运维一体化、微服务架构。

DevOps 最初的出现打破了开发人员和运维人员之间历来存在的壁垒和沟鸿,加强了开发、运营和质量保证人员之间的沟通、协作与整合。在后 DevOps 时代,我们可以借助容器技术更快地对应用进行迭代上线。

下面是应用发布的一般过程,开发者 push 代码,触发构建,构建过程是拉取源码,应用打包,容器镜像推送,部署。

这个模型其实已经有很多地方充分利用了云原生的优势,比如容器技术、Kubernetes、动态分配 slave pod 等。但还有一些挑战。

如何应用在环境栈之间的安全推进发布

如何管理应用发布的权限和安全审批

如何提高应用的平均部署时间和平均恢复时间

如何迅速对线上应用进行故障定位、复现和回滚

云原生时代下的 DevOps 之道

首先我们要充分利用云原生技术的优势,云原生可以改进应用开发的效率,改变企业的组织结构,甚至会在文化层面上直接影响一个公司的决策。在容器领域内,Kubernetes 已经成为了容器编排和管理的社区标准。它通过把应用服务抽象成多种资源类型,比如 Deployment、Service 等,提供了一个云原生应用通用的可移植模型。

在这样的背景下,我们如何在云原生的环境下实践更高效的 DevOps 来达到更有生产力的表现就成为了一个新的课题和诉求。

下面是一个企业应用平台的建设目标:

在此 PaaS 平台的基础上,我们设计了 GitOps 安全发布模型来解决前面我们提到的一些挑战。

在设计 GitOps 发布模型的时候是有以下这些核心诉求的:

版本管理。我们希望每一个发布的应用的版本号都能跟 git commit id 关联,这样的好处就是每一个变更都有历史记录查询、可以更快进行故障定位和修复;

基线管理。便于问题复现和快速回滚;

安全发布。包括发布权限管理以及安全审批的内容;

快速反馈。提高研发效能。

GitOps 发布模型有以下特性:

Git 仓库是任何 CICD 过程的唯一输入源

声明式的应用编排、构建部署模型

应用在环境栈之间的无差别、自动化推进

PR/MR 触发的拉取式流水线过程

快速反馈机制

下面是使用 GitOps 管理应用发布到不同 Kubernetes 集群的架构图。

首先是应用源码与构建源码分离,我们可以看到橙色框起来的这两个源码项目,一个是我们的应用源码项目 application-java-demo, 左侧的这个源码项目是用来存放构建源码的,比如 preview pipeline 的 Jenkinsfile, staging pipeline 的 Jenkinsfile,production pipeline 的 Jenkinsfile, 除了 Jenkinsfile 之外,可能还有一些关于动态创建测试环境、连接预发环境或者生产环境的敏感信息,这些敏感信息也可以存放在数据库里,然后这里保存数据库的连接信息。

这个普通应用 application-java-demo 在 Git 仓库里是有不同的分支的,每个分支跟 Kubernetes 集群环境都有一定的对应关系,比如我们这里的设定,master 分支对应的是生产环境,latest 分支对应的是预发环境,其他开发分支对应的是测试环境,测试环境的动态创建和销毁、应用再测试环境的部署发布是开发测试人员自助的服务,但应用想要部署到预发环境和生产环境中的话是需要经过管理员安全审批的。

普通开发者的权限只有创建新代码分支和创建合并请求的权限,除此之外,剩下其他的部分都是管理员才有权限做的,绿色区域是 Jenkins 的流水线任务,当然你也可以是使用其他 cicd 引擎来做这个流水线任务的构建。普通开发者没有 Jenkins 环境的创建 Job 和构建 Job 的权限,也没有更改配置的权限,他有的只是构建 Job 的日志查看权限。

最后是一个时序图,演示一个应用从开发测试到业务上线迭代的一个完整流程:

开发者提交新的功能分支 feature;

开发者创建请求合并代码到 latest 分支的 Merge Request;

开发者创建 Merge Request 的动作自动触发名为 preview-pipeline 的 Jenkins 流水线任务的构建;

preview-pipeline 流水线任务从 Git 服务器拉取 preview-pipeline 源码项目,并按照项目中 Jenkinsfile 文件中的声明式脚本运行源码编译、测试、容器镜像构建和推送、应用部署到 Preview 的容器集群、钉钉通知的流程;

管理员在 Git 服务器的 Merge Request 页面查看应用的预览连接并验证应用是否可以合并到 latest 分支,如果通过验证则接受 Merge Request 的合并,触发步骤 6, 如果不通过则通知开发者进行代码更新和提交,退回步骤 1;

管理员接受 Merge Request 合并的动作会自动触发 Jenkins 流水线任务 staging-pipeline 的构建;

staging-pipeline 流水线任务从 Git 服务器拉取 staging-pipeline 源码项目,并按照项目中 Jenkinsfile 文件中的声明式脚本运行源码编译、测试、容器镜像构建和推送、应用部署到 Staging 的容器集群、钉钉通知的流程;

Staging 环境中的应用服务在通过测试和验证后,管理员可以合并 latest 分支到 master 分支;

管理员合并 latest 分支到 master 分支后,会自动触发 Jenkins 流水线任务 production-pipeline 的构建;

production-pipeline 流水线任务从 Git 服务器拉取 production-pipeline 源码项目,并按照项目中 Jenkinsfile 文件中的声明式脚本运行源码编译、测试、容器镜像构建和推送、应用部署到 Production 的容器集群、钉钉通知的流程。

GitOps 是一套方法论,所以其实是有多种实践的方式的,会有多种多样的好用的工具,比如使用 draft 可以帮助完成应用编排模板的自动化生成,skaffold 用来简化应用构建部署流程,kaniko 可以实现不依赖 docker daemon 的镜像构建和推送,helm 用作应用的包管理工具,还有其他 cicd 引擎像 jenkins,tekton,argo 以及为云原生而生的 jenkinsx 等等。

后面,我们会单独实战演示 GitOps 安全发布模型的工作过程。

查看更多:

上云就看云栖号:更多云资讯,上云案例,最佳实践,产品入门,访问:

TL5728-IDK是一款广州创龙基于SOM-TL5728核心板设计的开发板,底板采用沉金无铅工艺的4层板设计,它为用户提供了SOM-TL5728核心板的测试平台,用于快速评估SOM-TL5728核心板的整体性能。

QSPI Flash

采用存取速度快速的QSPI Flash,内存为32MByte。

加密芯片

采用高安全性的ATAES132,为串行电子可擦写和可编程只读存储器(EEPROM)提供了验证和机密的非易失性存储性能.

2020年,刚刚开始WordPress博客系统被网站安全检测出有插件绕过漏洞,该插件的开发公司,已升级了该插件并发布1.7版本,对以前爆出的漏洞进行了修补,该企业网站漏洞造成的原因是未经许可身份认证的普通用户给以了系统管理员权限。黑客能够以网站管理员的身份进行登陆,并可以将wp企业网站的全部数据表信息恢复为以前的模式,进而上传webshell企业网站木马代码来进行篡改企业网站。现阶段受危害的版本包含最新的WP系统。

这个WP插件的主要功能是可以将网站的主题自定义的进行外观设计,与导入代码,让很多新手不懂代码设计的可以迅速的掌握该技巧对网站进行外观设计,目前全球用该插件的人数达到二十五万多企业网站在使用该插件,也是目前最受环境的插件。该网站漏洞影响的插件版本,是存在于1.5-1.6版本。根据目前WP官方的数据资料统计,使用该版本的用户以及网站数量占比竟然达到百分之95左右,受漏洞影响的网站确实太多,建议各位站长尽快对该插件进行升级,修复漏洞。

该网站漏洞的利用方式以及条件,必须是该主题插件处于启用状态,并且是公司网站上都安装了这个插件才会受到漏洞的攻击,让黑客有攻击网站的机会。SINE安全技术在实际的漏洞利用测试过程中,也发现了一些问题,插件绕过漏洞的利用前提是需要有1个条件来进行,网站的数据库表中的普通用户必须有admin账户存在,目前的网站安全解决方案是尽快升级该插件到最新版本,有些企业网站不知道该如何升级的,先将改插件在后台关闭掉,防止黑客的入侵。

针对该插件漏洞的修复办法,可以在“wdcp_init”的Hook在网站环境中运行,而且还可启用无需通过身份认证的普通用户的“/wp-wdcp/wdcp-ajax.tp框架”。缺少身份认证就使漏洞没有利用的机会了。假如数据表中存有“wdcp”普通用户,未经许可身份认证的黑客机会会应用此账号登陆,并删掉全部以已定义的数据表前缀打头的。可以将该用户删除掉,以防止网站被攻击。

只要删掉了全部表,它将应用高级设置和数据信息添充数据表,随后将“wdcp”普通用户的密码修改为其此前已经知道的登陆密码。某安全组织于2月6号检测到了该网站插件绕过漏洞,在同一天将其安全报告给插件的开发公司。十天之后,也就是上个星期,主题Grill插件公司,发布了修复该网站漏洞的新版本。

在编写这篇文章时,修补后的插件,最新版本下载数量达到二十多万,这说明应用还有很多企业网站没有修复漏洞,仍然处在被攻击的风险当中。针对于WP官方的数据安全中心发布的安全报告中显示的两个网站漏洞,当黑客利用这些网站漏洞时,都是会造成和本次安全事件一样的影响。建议使用该插件的wordpress公司网站尽快升级,修复漏洞,以免对网站对公司产生更大的经济损失以及影响。

在其中1个CVE-2020-7048准许未经许可身份认证的普通用户从其他数据表中重置表,而另外一个CVE-2020-7047则是赋予最低管理权限的账号网站管理员管理权限。如果您对网站代码不是太了解,不知道该如何修复wordpress的漏洞,或者是您网站使用的是wp系统开发的,被黑客攻击篡改数据,也可以找专业的网站安全公司来处理解决。

非常时期,企业和员工都面临着巨大的压力。因此,很多企业(特别是互联网类公司)选择让员工在家线上办公,开启“在家办公模式”。

在抗击疫情的新形势下,诸如线上办公、居家办公、视频会议等“云办公”方式将成为维持企业和社会良性运行的重要一环。

远程办公:RPA助力优化业务流程

受益于近年云计算的高速发展,远程办公在此次疫情期间发挥了重要作用。

单从能效角度看,若远程办公与企业原本集体办公的效率相差无几的话,想必其将会成为企业降低成本行之有效的方式之一。那么在疫情过后很可能会催生一个类似“业务外包”的新雇佣方式。

之后,企业会将可通过远程办公完成的非核心业务放到外部,把这部分业务交给在家、咖啡馆等地办公的职员。当这部分“外围”业务剥离足够多之后,随着办公人员数量的减少,企业便可以租用更小的办公场地以节省运营支出,达到降低成本的目的。

同时,不管是核心业务还是外包业务,企业同样会追求最大的效率输出,要精简岗位开源节流,业务流程优化不可避免,RPA自然会成为企业选择的解决方案之一。

企业可以在各单元的业务流程中启用RPA方案,提高员工办公效率与价值,最终得以实现降本增效。

SaaS应用增长,云型RPA的春天

原本很多企业认为办公OA、远程会议等可有可无,现在看来却是有备无患。疫情期间很多企业不得已而选择远程办公,实则是SaaS应用真正走上台面的一个契机。

大量企业开始使用SaaS,对云型RPA厂商而言将是一个绝佳的机会。

远程办公使得办公自动化等业务也成为企业刚需,云计算服务商将RPA机器人融入整体解决方案(譬如在办公OA、ERP等产品中加入RPA功能)供给企业使用。

RPA厂商也可以基于RPA推出基于办公场景的各种解决方案。一旦用户体验到RPA的好处,便会持续应用。待此需求足够大之后,RPA必然会成为标配,基于此可以推出各种增值服务。

在具体落地方面,相对于本地部署的RPA软件产品,无需施工人员到场,企业技术人员通过简单学习就能在线部署RPA各种功能,随时可以使用。

原文地址:

RPA:

RPA机器人:

简单工厂模式

符合人类思考,由工厂创建并返回对象

工厂方法模式

符合开闭原则,不用修改if-else语句,直接通过继承abstractFactory来扩展新产品工厂。

抽象工厂模式

如果新增一种产品的话,通过在AbstractFactory里面新增createProductXX方法