项目调研:Swagger在PHP项目中的使用实践

zkbhj 发表了文章 • 0 个评论 • 4680 次浏览 • 2018-01-17 12:17 • 来自相关话题

上一篇文章我们一起学习了什么是swagger以及如何用官方提供的标准规范和语言(Json或者YAML)来生成我们的接口文档。但是我们部门的项目都是PHP开发的,要接入这个swagger,应该怎么做呢?今天这篇文章就是来做这个实践!
 
上一篇说的那么官方的介绍,但是这个swagger到底是个什么鬼呢?其实,说白了理解起来也很简单:

其实就是安装Swagger套件, 然后API代码里写注释, 用Swagger后端程序跑API来提取注释, 生成一个json文件(或者YAML), 再通关Swagger前端来美化,整理JSON数据.

 
所以,看到这里,我们就知道了。要使用Swagger需要安装2个东西:
 
前端,用来显示;后端, 用来生成JSON
 
1, 安装前端
 
swagger-ui下载
git clone https://github.com/swagger-api/swagger-ui.git下载之后找到dist目录, 打开index.html把其中的那一串url改成自己的, 比如 http//localhost/yii2/swagger-docs/swagger.json
 
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://localhost/yii2/swagger- ... 3B%3B
}还可以把界面调整成中文, 放开js文件的注释即可
<script src='lang/translator.js' type='text/javascript'></script>
<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
<script src='lang/zh-cn.js' type='text/javascript'></script>然后打开URL就可以看到前端界面了, 应该是没内容的, 因为还没生成swagger.json, 生成好之后你设置的URL就起了作用, 直接访问前端就好
http://localhost/yii2/swagger-ui/dist/index.html2, 安装后端
git clone https://github.com/zircote/swagger-php.git因为公司用的是yii2, 所以使用了composer来安装
"require": { "zircote/swagger-php": "*" }之后composer update, 或者直接命令行, 详见官方安装指南。
DCdeMacBook-Pro:yii2 DC$ php composer.phar require zircote/swagger-php把swagger-php放在根目录下,然后用官方提供的Examples来生成测试json
cd swagger-php
mkdir doc
php swagger.phar Examples -o doc"-o" 前面代表API源目录, 即你想要生成哪个目录的API文档, 你的项目代码目录. "-o" 后面是生成到哪个path。我没有进入到swagger-php下面, 直接打的命令行, 任意路径下都可以执行生成json操作
php /Users/DC/www/yii2/vendor/zircote/swagger-php/bin/swagger /Users/DC/www/yii2/vendor/zircote/swagger-php/Examples -o /Users/DC/www/yii2/swagger-docs
然后再看http//localhost/yii2/swagger-ui/dist/index.html, 生成了API文档。
 
准备工作都做好了, 那就写代码注释就行了, 注释怎么写? 官方参考文档
 
比如Model:
/**
* @SWG\Model(
* id="vps",
* required="['type', 'hostname']",
* @SWG\Property(name="hostname", type="string"),
* @SWG\Property(name="label", type="string"),
* @SWG\Property(name="type", type="string", enum="['vps', 'dedicated']")
* )
*/
class HostVps extends Host implements ResourceInterface
{
// ...
}
比如Controller:
/**
* @SWG\Resource(
* basePath="http://skyapi.dev",
* resourcePath="/vps",
* @SWG\Api(
* path="/vps",
* @SWG\Operation(
* method="GET",
* type="array",
* summary="Fetch vps lists",
* nickname="vps/index",
* @SWG\Parameter(
* name="expand",
* description="Models to expand",
* paramType="query",
* type="string",
* defaultValue="vps,os_template"
* )
* )
* )
* )
*/
class VpsController extends Controller
{
// ...
}每次改动API代码注释之后都要手动生成json文件? 太麻烦了, 写了个controller, 每次访问swagger-ui的这个controller, 先生成json再跳转到ui页面。
$b2broot = Yii::getAlias('@b2broot');
$swagger = \Swagger\scan($b2broot.'/myapi');
$json_file = $b2broot.'/swagger-docs/swagger.json';
$is_write = file_put_contents($json_file, $swagger);
if ($is_write == true) {
$this->redirect('http//localhost/yii2/swagger-ui/dist/index.html');
}参考文档:
https://www.cnblogs.com/handongyu/p/6992367.html
  查看全部
上一篇文章我们一起学习了什么是swagger以及如何用官方提供的标准规范和语言(Json或者YAML)来生成我们的接口文档。但是我们部门的项目都是PHP开发的,要接入这个swagger,应该怎么做呢?今天这篇文章就是来做这个实践!
 
上一篇说的那么官方的介绍,但是这个swagger到底是个什么鬼呢?其实,说白了理解起来也很简单:


其实就是安装Swagger套件, 然后API代码里写注释, 用Swagger后端程序跑API来提取注释, 生成一个json文件(或者YAML), 再通关Swagger前端来美化,整理JSON数据.


 
所以,看到这里,我们就知道了。要使用Swagger需要安装2个东西:
 
  1. 前端,用来显示;
  2. 后端, 用来生成JSON

 
1, 安装前端
 
swagger-ui下载
git clone https://github.com/swagger-api/swagger-ui.git
下载之后找到dist目录, 打开index.html把其中的那一串url改成自己的, 比如 http//localhost/yii2/swagger-docs/swagger.json
 
$(function () {
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://localhost/yii2/swagger- ... 3B%3B
}
还可以把界面调整成中文, 放开js文件的注释即可
<script src='lang/translator.js' type='text/javascript'></script>
<!-- <script src='lang/ru.js' type='text/javascript'></script> -->
<script src='lang/zh-cn.js' type='text/javascript'></script>
然后打开URL就可以看到前端界面了, 应该是没内容的, 因为还没生成swagger.json, 生成好之后你设置的URL就起了作用, 直接访问前端就好
http://localhost/yii2/swagger-ui/dist/index.html
2, 安装后端
git clone https://github.com/zircote/swagger-php.git
因为公司用的是yii2, 所以使用了composer来安装
"require": { "zircote/swagger-php": "*" }
之后composer update, 或者直接命令行, 详见官方安装指南
DCdeMacBook-Pro:yii2 DC$ php composer.phar require zircote/swagger-php
把swagger-php放在根目录下,然后用官方提供的Examples来生成测试json
cd swagger-php
mkdir doc
php swagger.phar Examples -o doc
"-o" 前面代表API源目录, 即你想要生成哪个目录的API文档, 你的项目代码目录. "-o" 后面是生成到哪个path。我没有进入到swagger-php下面, 直接打的命令行, 任意路径下都可以执行生成json操作
php /Users/DC/www/yii2/vendor/zircote/swagger-php/bin/swagger /Users/DC/www/yii2/vendor/zircote/swagger-php/Examples -o /Users/DC/www/yii2/swagger-docs
然后再看http//localhost/yii2/swagger-ui/dist/index.html, 生成了API文档。
 
准备工作都做好了, 那就写代码注释就行了, 注释怎么写? 官方参考文档
 
比如Model:
/**
* @SWG\Model(
* id="vps",
* required="['type', 'hostname']",
* @SWG\Property(name="hostname", type="string"),
* @SWG\Property(name="label", type="string"),
* @SWG\Property(name="type", type="string", enum="['vps', 'dedicated']")
* )
*/
class HostVps extends Host implements ResourceInterface
{
// ...
}

比如Controller:
/**
* @SWG\Resource(
* basePath="http://skyapi.dev",
* resourcePath="/vps",
* @SWG\Api(
* path="/vps",
* @SWG\Operation(
* method="GET",
* type="array",
* summary="Fetch vps lists",
* nickname="vps/index",
* @SWG\Parameter(
* name="expand",
* description="Models to expand",
* paramType="query",
* type="string",
* defaultValue="vps,os_template"
* )
* )
* )
* )
*/
class VpsController extends Controller
{
// ...
}
每次改动API代码注释之后都要手动生成json文件? 太麻烦了, 写了个controller, 每次访问swagger-ui的这个controller, 先生成json再跳转到ui页面。
$b2broot = Yii::getAlias('@b2broot');
$swagger = \Swagger\scan($b2broot.'/myapi');
$json_file = $b2broot.'/swagger-docs/swagger.json';
$is_write = file_put_contents($json_file, $swagger);
if ($is_write == true) {
$this->redirect('http//localhost/yii2/swagger-ui/dist/index.html');
}
参考文档:
https://www.cnblogs.com/handongyu/p/6992367.html
 

项目调研:Swagger - RESTFUL接口的文档在线自动生成功能测试框架

zkbhj 发表了文章 • 0 个评论 • 2241 次浏览 • 2018-01-17 12:03 • 来自相关话题

前言

Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。本文简单介绍了在项目中集成swagger的方法和一些常见问题。如果想深入分析项目源码,了解更多内容,见参考资料。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。
 

 
一、基础介绍
 
1.1、什么是 Swagger?

The World's Most Popular Framework for APIs.

Starting January 1st 2016 the Swagger Specification has been donated(捐赠) to the Open API Initiative (OAI) and is the foundation of the OpenAPI Specification.


Swagger(丝袜哥)给人第一印象就是【最(hen)流(niu)行(bai)】,不懂Swagger咱就out了。它的官方网站是http://swagger.io/。

Swagger是一个简单但功能强大的API表达工具。它具有地球上最大的API工具生态系统,数以千计的开发人员,使用几乎所有的现代编程语言,都在支持和使用Swagger。使用Swagger生成API,我们可以得到交互式文档,自动生成代码的SDK以及API的发现特性等。

现在,Swagger已经帮助包括Apigee, Getty图像, Intuit, LivingSocial, McKesson, 微软, Morningstar和PayPal等世界知名企业建立起了一套基于RESTful API的完美服务系统。

2.0版本已经发布,Swagger变得更加强大。值得感激的是,Swagger的源码100%开源在 github。
 
1.2 OpenAPI规范
 
OpenAPI规范是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。OpenAPI规范帮助我们描述一个API的基本信息,比如:
有关该API的一般性描述可用路径(/资源)在每个路径上的可用操作(获取/提交...)每个操作的输入/输出格式
 
目前V2.0版本的OpenAPI规范(也就是SwaggerV2.0规范)已经发布并开源在github上。该文档写的非常好,结构清晰,方便随时查阅。关于规范的学习和理解,本文最后还有个彩蛋。
 
1.3 为啥要使用OpenAPI规范?
OpenAPI规范这类API定义语言能够帮助你更简单、快速的表述API,尤其是在API的设计阶段作用特别突出根据OpenAPI规范编写的二进制文本文件,能够像代码一样用任何VCS工具管理起来一旦编写完成,API文档可以作为:
       需求和系统特性描述的根据
       前后台查询、讨论、自测的基础
       部分或者全部代码自动生成的根据
       其他重要的作用,比如开放平台开发者的手册...
 
1.4 如何编写API文档?

1.4.1 语言:JSON vs YAML

我们可以选择使用JSON或者YAML的语言格式来编写API文档。但是个人建议使用YAML来写,原因是它更简单。一图胜千言,先看用JSON写的文档:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Simple API",
"description": "A simple API to learn how to write OpenAPI Specification"
},
"schemes": [
"https"
],
"host": "simple.api",
"basePath": "/openapi101",
"paths": {
"/persons": {
"get": {
"summary": "Gets some persons",
"description": "Returns a list containing all persons.",
"responses": {
"200": {
"description": "A list of Person",
"schema": {
"type": "array",
"items": {
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
}
}
}
}
}
}
}再来看看同一份API文档的YAML实现:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons.
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string对于普通人来说,似乎用YAML更能够简化书写和阅读。这里我们并没有非此即彼的选择问题,因为:
几乎所用支持OpenAPI规范的工具都支持YAML有很多的工具可以实现YAML-JSON之间的转换
 
所以,用自己喜欢的方式书写即可。(后面的示例文档也都是用YAML来写的。强烈推荐使用YAML。)
 
1.4.2 编辑器
 
编写API文档,其实我们只是在写一个简单的文本文件。我们可以用任何最普通的文档编辑器来写。但是为了提高效率,还是建议使用专业的编辑工具。众多工具中,最好的选择是Swagger Editor,它能够提供语法高亮、自动完成、即时预览等功能,非常强大。左边编辑API文档,右边实时预览。下面这张动图展示编辑功能。





 
我们可以使用在线版本来编辑,也可以非常简单的本地部署,更多细节请参考另一篇文档开源仓库上的说明。
 
二、从零开始
 
这一章主要介绍API的基本组成部分,包括提供给API消费者(所有可能访问API的个体,下简称“消费者”)的不同HTTP请求方法、路径,请求和消息体中的参数,以及返回给消费者的不同HTTP状态及响应消息体。
 
2.1 最简单的例子
 
我们从一个最简单(几乎没有东西)的API文档开始:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths: {}这个文档的内容分成四部分,下面分别来说明。

2.1.1 OpenAPI规范的版本号

首先我们要通过一个swagger属性来声明OpenAPI规范的版本。
swagger: "2.0"你没看错,是swagger,上面已经介绍了,OpenAPI规范是基于Swagger的,在未来的版本中,这个属性可能会换成别的。 目前这个属性的值,暂时只能填写为2.0。
 
2.1.2 API描述信息
 
然后我们需要说明一下API文档的相关信息,比如API文档版本(注意不同于上面的规范版本)、API文档名称已经可选的描述信息。
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification
2.1.3 API的URL
作为web API,一个很重要的信息就是用来给消费者使用的根URL,可以用协议(http或者https)、主机名、根路径来描述:schemes:
- https
host: simple.api
basePath: /openapi101这这个例子中,消费者把https//simple.api/open101作为根节点来访问各种API。因为和具体环境有关,不涉及API描述的根本内容,所以这部分信息是可选的。
 
2.1.4 API的操作(operation)
 
这个例子中,我们没有写API的操作,用一个YAML的空对象{}先占个位置。
 2.2 定义一个API操作
 
如果我们要展示一组用户信息,可以这样描述:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons.
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string2.2.1 添加一个路径(path)
 
我们添加一个/persons的路径,用来访问一组用户信息:
paths:
/persons:2.2.2 在路径中添加一个HTTP方法
 
在每个路径中,我们可以添加任意的HTTP动词来操作所需要的资源。

比如需要展示一组用户信息,我们可以在/persons路径中添加get方法,同时还可以填写一些简单的描述信息(summary)或者说明该方法的一段长篇大论(description)。
  get:
summary: Gets some persons
description: Returns a list containing all persons.
这样一来,我们调 get https//simple.api/open101/persons方法就能获取一个用户信息列表了。
2.2.3 定义响应(response)类型
 
对于每个方法(或操作),我们都可以在响应(responses)中添加任意的HTTP状态码(比如200 OK 或者 404 Not Found等)。这个例子中我们添加上200的响应:responses:
200:
description: A list of Person
2.2.4 定义响应内容
 get /persons这个接口返回一组用户信息,我们通过响应消息中的模式(schema)属性来描述清楚具体的返回内容。

一组用户信息就是一个用户信息对象的数组(array),每一个数组元素则是一个用户信息对象(object),该对象包含三个string类型的属性:姓氏、名字、用户名,其中用户名必须提供(required)。
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string2.3 定义请求参数(query parameters)
 用户太多,我们不想一股脑全部输出出来。这个时候,分页输出是个不错的选择,我们可以通过添加请求参数来实现。
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
#START############################################################################
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
# END ############################################################################
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string2.3.1 在get方法中增加请求参数
 
首先我们在 get 方法中增加一个参数属性:
paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
#START############################################################################
parameters:
# END ############################################################################2.3.2 添加分页参数

在参数列表中,我们添加两个名字(name)分别叫做pageSize和pageNumber的整型(integer)参数,并作简单描述: parameters:
#START############################################################################
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
# END ##############################################################################
responses:这样一来,消费者就可以通过 get /persons?pageSize=20&pageNumber=2 来访问第2页的用户信息(不超过20条)了。
 
2.4 定义路径参数(path parameter)
 
有时候我们想要根据用户名来查找用户信息,这时我们需要增加一个接口操作,比如可以添加一个类似 /persons/{username} 的操作来获取用户信息。注意,{username} 是在请求路径中的参数。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string

#START############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
404:
description: The Person does not exists.
# END ############################################################################2.4.1 添加一个 get /persons/{username} 操作

首先我们在 /persons 路径后面,增加一个 /persons/{username} 的路径,并定义一个 get (操作)方法。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
****
username:
type: string
#START############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username
# END ############################################################################2.4.2 定义路径参数 username
 
因为 {username} 是路径参数,我们需要先像请求参数一样将它添加到 parameters 属性中,注意名称应该同上面大括号( { } ) 里面的名称一致。并通过 in 这个属性,来表示它是一个路径(path)参数。
parameters:
- name: username
in: path
required: true
description: The person's username
type: string定义路径参数时很容易出现的问题就是忘记:required: true,Swagger的自动完成功能中没有包含这个属性定义。 如果没有写 require 属性,默认值是 false,也就是说 username 参数时可选的。可事实上,作为路径参数,它是必需的。
 
2.4.3 定义响应消息

别忘了获取单个用户信息也需要填写 200 响应消息,响应消息体的内容就是之前描述过的用户信息(用户信息列表中的一个元素):
 
responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string当然,API的提供者会对 username 进行校验,如果查无此人,应该返回 404 的异常状态。所以我们再加上 404 状态的响应:
404:
description: The Person does not exists.
2.5 定义消息体参数(body parameter)
 
当我们需要添加一个用户信息时,我们需要一个能够提供 post /persons 的API操作。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
#START############################################################################
post:
summary: Creates a person
description: Adds a new person to the persons list.
parameters:
- name: person
in: body
description: The person to create.
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
responses:
204:
description: Persons succesfully created.
400:
description: Persons couldn't have been created.
# END ############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username.
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
404:
description: The Person does not exists.
  查看全部
前言

Swagger 是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。本文简单介绍了在项目中集成swagger的方法和一些常见问题。如果想深入分析项目源码,了解更多内容,见参考资料。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。
 

 
一、基础介绍
 
1.1、什么是 Swagger?


The World's Most Popular Framework for APIs.

Starting January 1st 2016 the Swagger Specification has been donated(捐赠) to the Open API Initiative (OAI) and is the foundation of the OpenAPI Specification.



Swagger(丝袜哥)给人第一印象就是【最(hen)流(niu)行(bai)】,不懂Swagger咱就out了。它的官方网站是http://swagger.io/

Swagger是一个简单但功能强大的API表达工具。它具有地球上最大的API工具生态系统,数以千计的开发人员,使用几乎所有的现代编程语言,都在支持和使用Swagger。使用Swagger生成API,我们可以得到交互式文档,自动生成代码的SDK以及API的发现特性等。

现在,Swagger已经帮助包括Apigee, Getty图像, Intuit, LivingSocial, McKesson, 微软, Morningstar和PayPal等世界知名企业建立起了一套基于RESTful API的完美服务系统。

2.0版本已经发布,Swagger变得更加强大。值得感激的是,Swagger的源码100%开源在 github
 
1.2 OpenAPI规范
 
OpenAPI规范是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。OpenAPI规范帮助我们描述一个API的基本信息,比如:
  • 有关该API的一般性描述
  • 可用路径(/资源)
  • 在每个路径上的可用操作(获取/提交...)
  • 每个操作的输入/输出格式

 
目前V2.0版本的OpenAPI规范(也就是SwaggerV2.0规范)已经发布并开源在github上。该文档写的非常好,结构清晰,方便随时查阅。关于规范的学习和理解,本文最后还有个彩蛋。
 
1.3 为啥要使用OpenAPI规范?
  • OpenAPI规范这类API定义语言能够帮助你更简单、快速的表述API,尤其是在API的设计阶段作用特别突出
  • 根据OpenAPI规范编写的二进制文本文件,能够像代码一样用任何VCS工具管理起来
  • 一旦编写完成,API文档可以作为:

       需求和系统特性描述的根据
       前后台查询、讨论、自测的基础
       部分或者全部代码自动生成的根据
       其他重要的作用,比如开放平台开发者的手册...
 
1.4 如何编写API文档?

1.4.1 语言:JSON vs YAML

我们可以选择使用JSON或者YAML的语言格式来编写API文档。但是个人建议使用YAML来写,原因是它更简单。一图胜千言,先看用JSON写的文档:
{
"swagger": "2.0",
"info": {
"version": "1.0.0",
"title": "Simple API",
"description": "A simple API to learn how to write OpenAPI Specification"
},
"schemes": [
"https"
],
"host": "simple.api",
"basePath": "/openapi101",
"paths": {
"/persons": {
"get": {
"summary": "Gets some persons",
"description": "Returns a list containing all persons.",
"responses": {
"200": {
"description": "A list of Person",
"schema": {
"type": "array",
"items": {
"properties": {
"firstName": {
"type": "string"
},
"lastName": {
"type": "string"
},
"username": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
再来看看同一份API文档的YAML实现:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons.
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
对于普通人来说,似乎用YAML更能够简化书写和阅读。这里我们并没有非此即彼的选择问题,因为:
  • 几乎所用支持OpenAPI规范的工具都支持YAML
  • 有很多的工具可以实现YAML-JSON之间的转换

 
所以,用自己喜欢的方式书写即可。(后面的示例文档也都是用YAML来写的。强烈推荐使用YAML。)
 
1.4.2 编辑器
 
编写API文档,其实我们只是在写一个简单的文本文件。我们可以用任何最普通的文档编辑器来写。但是为了提高效率,还是建议使用专业的编辑工具。众多工具中,最好的选择是Swagger Editor,它能够提供语法高亮、自动完成、即时预览等功能,非常强大。左边编辑API文档,右边实时预览。下面这张动图展示编辑功能。

QQ截图20180117111021.jpg

 
我们可以使用在线版本来编辑,也可以非常简单的本地部署,更多细节请参考另一篇文档开源仓库上的说明。
 
二、从零开始
 
这一章主要介绍API的基本组成部分,包括提供给API消费者(所有可能访问API的个体,下简称“消费者”)的不同HTTP请求方法、路径,请求和消息体中的参数,以及返回给消费者的不同HTTP状态及响应消息体。
 
2.1 最简单的例子
 
我们从一个最简单(几乎没有东西)的API文档开始:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths: {}
这个文档的内容分成四部分,下面分别来说明。

2.1.1 OpenAPI规范的版本号

首先我们要通过一个swagger属性来声明OpenAPI规范的版本。
swagger: "2.0"
你没看错,是swagger,上面已经介绍了,OpenAPI规范是基于Swagger的,在未来的版本中,这个属性可能会换成别的。 目前这个属性的值,暂时只能填写为2.0。
 
2.1.2 API描述信息
 
然后我们需要说明一下API文档的相关信息,比如API文档版本(注意不同于上面的规范版本)、API文档名称已经可选的描述信息。
info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

2.1.3 API的URL
作为web API,一个很重要的信息就是用来给消费者使用的根URL,可以用协议(http或者https)、主机名、根路径来描述:
schemes:
- https
host: simple.api
basePath: /openapi101
这这个例子中,消费者把https//simple.api/open101作为根节点来访问各种API。因为和具体环境有关,不涉及API描述的根本内容,所以这部分信息是可选的。
 
2.1.4 API的操作(operation)
 
这个例子中,我们没有写API的操作,用一个YAML的空对象{}先占个位置。
 2.2 定义一个API操作
 
如果我们要展示一组用户信息,可以这样描述:
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons.
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
2.2.1 添加一个路径(path)
 
我们添加一个/persons的路径,用来访问一组用户信息:
paths:
/persons:
2.2.2 在路径中添加一个HTTP方法
 
在每个路径中,我们可以添加任意的HTTP动词来操作所需要的资源。

比如需要展示一组用户信息,我们可以在/persons路径中添加get方法,同时还可以填写一些简单的描述信息(summary)或者说明该方法的一段长篇大论(description)。
 
    get:
summary: Gets some persons
description: Returns a list containing all persons.

这样一来,我们调 get https//simple.api/open101/persons方法就能获取一个用户信息列表了。
2.2.3 定义响应(response)类型
 
对于每个方法(或操作),我们都可以在响应(responses)中添加任意的HTTP状态码(比如200 OK 或者 404 Not Found等)。这个例子中我们添加上200的响应:
responses:
200:
description: A list of Person

2.2.4 定义响应内容
 get /persons这个接口返回一组用户信息,我们通过响应消息中的模式(schema)属性来描述清楚具体的返回内容。

一组用户信息就是一个用户信息对象的数组(array),每一个数组元素则是一个用户信息对象(object),该对象包含三个string类型的属性:姓氏、名字、用户名,其中用户名必须提供(required)。
 schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
2.3 定义请求参数(query parameters)
 用户太多,我们不想一股脑全部输出出来。这个时候,分页输出是个不错的选择,我们可以通过添加请求参数来实现。
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
#START############################################################################
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
# END ############################################################################
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
2.3.1 在get方法中增加请求参数
 
首先我们在 get 方法中增加一个参数属性:
paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
#START############################################################################
parameters:
# END ############################################################################
2.3.2 添加分页参数

在参数列表中,我们添加两个名字(name)分别叫做pageSize和pageNumber的整型(integer)参数,并作简单描述:
      parameters:
#START############################################################################
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
# END ##############################################################################
responses:
这样一来,消费者就可以通过 get /persons?pageSize=20&pageNumber=2 来访问第2页的用户信息(不超过20条)了。
 
2.4 定义路径参数(path parameter)
 
有时候我们想要根据用户名来查找用户信息,这时我们需要增加一个接口操作,比如可以添加一个类似 /persons/{username} 的操作来获取用户信息。注意,{username} 是在请求路径中的参数。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string

#START############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
404:
description: The Person does not exists.
# END ############################################################################
2.4.1 添加一个 get /persons/{username} 操作

首先我们在 /persons 路径后面,增加一个 /persons/{username} 的路径,并定义一个 get (操作)方法。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
****
username:
type: string
#START############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username
# END ############################################################################
2.4.2 定义路径参数 username
 
因为 {username} 是路径参数,我们需要先像请求参数一样将它添加到 parameters 属性中,注意名称应该同上面大括号( { } ) 里面的名称一致。并通过 in 这个属性,来表示它是一个路径(path)参数。
      parameters:
- name: username
in: path
required: true
description: The person's username
type: string
定义路径参数时很容易出现的问题就是忘记:required: true,Swagger的自动完成功能中没有包含这个属性定义。 如果没有写 require 属性,默认值是 false,也就是说 username 参数时可选的。可事实上,作为路径参数,它是必需的。
 
2.4.3 定义响应消息

别忘了获取单个用户信息也需要填写 200 响应消息,响应消息体的内容就是之前描述过的用户信息(用户信息列表中的一个元素):
 
      responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
当然,API的提供者会对 username 进行校验,如果查无此人,应该返回 404 的异常状态。所以我们再加上 404 状态的响应:
        404:
description: The Person does not exists.

2.5 定义消息体参数(body parameter)
 
当我们需要添加一个用户信息时,我们需要一个能够提供 post /persons 的API操作。
 
swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths:
/persons:
get:
summary: Gets some persons
description: Returns a list containing all persons. The list supports paging.
parameters:
- name: pageSize
in: query
description: Number of persons returned
type: integer
- name: pageNumber
in: query
description: Page number
type: integer
responses:
200:
description: A list of Person
schema:
type: array
items:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
#START############################################################################
post:
summary: Creates a person
description: Adds a new person to the persons list.
parameters:
- name: person
in: body
description: The person to create.
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
responses:
204:
description: Persons succesfully created.
400:
description: Persons couldn't have been created.
# END ############################################################################
/persons/{username}:
get:
summary: Gets a person
description: Returns a single person for its username.
parameters:
- name: username
in: path
required: true
description: The person's username
type: string
responses:
200:
description: A Person
schema:
required:
- username
properties:
firstName:
type: string
lastName:
type: string
username:
type: string
404:
description: The Person does not exists.

 

使用Git命令合并分支代码

zkbhj 发表了文章 • 0 个评论 • 2796 次浏览 • 2017-12-05 11:05 • 来自相关话题

合并步骤:

1、进入要合并的分支(如开发分支合并到master,则进入master目录)git pull
2、查看所有分支是否都pull下来了git branch -a
3、使用merge合并开发分支git merge 分支名
4、查看合并之后的状态git status
5、有冲突的话,通过IDE解决冲突;
Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu)
$ git merge master
Auto-merging mobile/modules/ziroomer/controllers/LdxController.php
CONFLICT (content): Merge conflict in mobile/modules/ziroomer/controllers/LdxCon troller.php
Auto-merging mobile/config/main.php
Auto-merging crontab/controllers/ZiroomerController.php
CONFLICT (content): Merge conflict in crontab/controllers/ZiroomerController.php
Auto-merging common/config/prod/params.php
Automatic merge failed; fix conflicts and then commit the result.

Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu|MERGING)
$ git add mobile/modules/ziroomer/controllers/LdxController.php

Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu|MERGING)
$ git add crontab/controllers/ZiroomerController.php
6、解决冲突之后,将冲突文件提交暂存区git add 冲突文件
7、提交merge之后的结果git commit
如果不是使用git commit -m "备注" ,那么git会自动将合并的结果作为备注,提交本地仓库;

8、本地仓库代码提交远程仓库git push
git将分支合并到分支,将master合并到分支的操作步骤是一样的; 查看全部
合并步骤:

1、进入要合并的分支(如开发分支合并到master,则进入master目录)
git pull

2、查看所有分支是否都pull下来了
git branch -a

3、使用merge合并开发分支
git merge 分支名

4、查看合并之后的状态
git status 

5、有冲突的话,通过IDE解决冲突;
Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu)
$ git merge master
Auto-merging mobile/modules/ziroomer/controllers/LdxController.php
CONFLICT (content): Merge conflict in mobile/modules/ziroomer/controllers/LdxCon troller.php
Auto-merging mobile/config/main.php
Auto-merging crontab/controllers/ZiroomerController.php
CONFLICT (content): Merge conflict in crontab/controllers/ZiroomerController.php
Auto-merging common/config/prod/params.php
Automatic merge failed; fix conflicts and then commit the result.

Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu|MERGING)
$ git add mobile/modules/ziroomer/controllers/LdxController.php

Administrator@ZKBHJ-PC MINGW64 /i/GIT/activity2 (feature-ziruyu|MERGING)
$ git add crontab/controllers/ZiroomerController.php

6、解决冲突之后,将冲突文件提交暂存区
git add 冲突文件

7、提交merge之后的结果
git commit 

如果不是使用git commit -m "备注" ,那么git会自动将合并的结果作为备注,提交本地仓库;

8、本地仓库代码提交远程仓库
git push

git将分支合并到分支,将master合并到分支的操作步骤是一样的;

怎样查看系统的centos版本?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4718 次浏览 • 2017-11-21 10:24 • 来自相关话题

怎么在Excel里批量给一列数据加前缀?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 5450 次浏览 • 2017-11-17 17:48 • 来自相关话题

flume:高可用高可靠分布式海量日志采集聚合和传输系统

zkbhj 发表了文章 • 0 个评论 • 3638 次浏览 • 2017-11-16 10:04 • 来自相关话题

flume是分布式的日志收集系统,它将各个服务器中的数据收集起来并送到指定的地方去,比如说送到图中的HDFS,简单来说flume就是收集日志的。 





http://blog.csdn.net/a20114801 ... 44664 查看全部
flume是分布式的日志收集系统,它将各个服务器中的数据收集起来并送到指定的地方去,比如说送到图中的HDFS,简单来说flume就是收集日志的。 

QQ截图20171116100349.jpg

http://blog.csdn.net/a20114801 ... 44664

经常打开Excel会提示内存或存储空间不足,怎么解决?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4980 次浏览 • 2017-10-31 10:30 • 来自相关话题

学习笔记:redis持久化

zkbhj 发表了文章 • 0 个评论 • 1866 次浏览 • 2017-09-01 17:17 • 来自相关话题

Redis中数据存储模式有2种:
cache-onlypersistence

cache-only即只做为“缓存”服务,不持久数据,数据在服务终止后将消失,此模式下也将不存在“数据恢复”的手段,是一种安全性低/效率高/容易扩展的方式;
persistence即为内存中的数据持久备份到磁盘文件,在服务重启后可以恢复,此模式下数据相对安全。

对于persistence持久化存储,Redis提供了两种持久化方法:
Redis DataBase(简称RDB)Append-only file (简称AOF)

除了这两种方法,Redis在早起的版本还存在虚拟内存的方法,现在已经被废弃。
 
一、RDB概述

RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。 
优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能 缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候

这里说的这个执行数据写入到临时文件的时间点是可以通过配置来自己确定的,通过配置redis在n秒内如果超过m个key被修改这执行一次RDB操作。这个操作就类似于在这个时间点来保存一次Redis的所有数据,一次快照数据。所有这个持久化方法也通常叫做snapshots。

RDB默认开启,redis.conf中的具体配置参数如下:
#dbfilename:持久化数据存储在本地的文件 
dbfilename dump.rdb 
#dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下 dir ./ 
##snapshot触发的时机,save <seconds> <changes> 
##如下为900秒后,至少有一个变更操作,才会snapshot 
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度 
##可以通过“save “””来关闭snapshot功能 
#save时间,以下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储。 
save 900 1 
save 300 10 
save 60 10000 
 
##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等 
stop-writes-on-bgsave-error yes 
 
##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间 
rdbcompression yes
snapshot触发的时机,是有“间隔时间”和“变更次数”共同决定,同时符合2个条件才会触发snapshot,否则“变更次数”会被继续累加到下一个“间隔时间”上。snapshot过程中并不阻塞客户端请求。snapshot首先将数据写入临时文件,当成功结束后,将临时文件重名为dump.rdb。

使用RDB恢复数据: 
自动的持久化数据存储到dump.rdb后。实际只要重启redis服务即可完成(启动redis的server时会从dump.rdb中先同步数据)

客户端使用命令进行持久化save存储:./redis-cli -h ip -p port save
./redis-cli -h ip -p port bgsave
一个是在前台进行存储,一个是在后台进行存储。我的client就在server这台服务器上,所以不需要连其他机器,直接./redis-cli bgsave。由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。 
二、AOF概述

Append-only file,将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾部,在append操作返回后(已经写入到文件或者即将写入),才进行实际的数据变更,“日志文件”保存了历史所有的操作过程;当server需要数据恢复时,可以直接replay此日志文件,即可还原所有的操作过程。AOF相对可靠,它和mysql中bin.log、apache.log、zookeeper中txn-log简直异曲同工。AOF文件内容是字符串,非常容易阅读和解析。 
优点:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志修复;AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)。 缺点:AOF文件比RDB文件大,且恢复速度慢。

我们可以简单的认为AOF就是日志文件,此文件只会记录“变更操作”(例如:set/del等),如果server中持续的大量变更操作,将会导致AOF文件非常的庞大,意味着server失效后,数据恢复的过程将会很长;事实上,一条数据经过多次变更,将会产生多条AOF记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的;因为AOF持久化模式还伴生了“AOF rewrite”。 
 
AOF的特性决定了它相对比较安全,如果你期望数据更少的丢失,那么可以采用AOF模式。如果AOF文件正在被写入时突然server失效,有可能导致文件的最后一次记录是不完整,你可以通过手工或者程序的方式去检测并修正不完整的记录,以便通过aof文件恢复能够正常;同时需要提醒,如果你的redis持久化手段中有aof,那么在server故障失效后再次启动前,需要检测aof文件的完整性。

AOF默认关闭,开启方法,修改配置文件reds.conf:appendonly yes
##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能 
##只有在“yes”下,aof重写/文件同步等特性才会生效 appendonly yes 
##指定aof文件名称 appendfilename appendonly.aof 
##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec
 appendfsync everysec
##在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no”
 no-appendfsync-on-rewrite no 
##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”
 auto-aof-rewrite-min-size 64mb 
##相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比。 
##每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后 
##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。 
auto-aof-rewrite-percentage 100
 
AOF是文件操作,对于变更操作比较密集的server,那么必将造成磁盘IO的负荷加重;此外linux对文件操作采取了“延迟写入”手段,即并非每次write操作都会触发实际磁盘操作,而是进入了buffer中,当buffer数据达到阀值时触发实际写入(也有其他时机),这是linux对文件系统的优化,但是这却有可能带来隐患,如果buffer没有刷新到磁盘,此时物理机器失效(比如断电),那么有可能导致最后一条或者多条aof记录的丢失。通过上述配置文件,可以得知redis提供了3中aof记录同步选项:
always:每一条aof记录都立即同步到文件,这是最安全的方式,也以为更多的磁盘操作和阻塞延迟,是IO开支较大。everysec:每秒同步一次,性能和安全都比较中庸的方式,也是redis推荐的方式。如果遇到物理服务器故障,有可能导致最近一秒内aof记录丢失(可能为部分丢失)。no:redis并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据buffer填充情况/通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因OS配置有关。
 
其实,我们可以选择的太少,everysec是最佳的选择。如果你非常在意每个数据都极其可靠,建议你选择一款“关系性数据库”吧。 
AOF文件会不断增大,它的大小直接影响“故障恢复”的时间,而且AOF文件中历史操作是可以丢弃的。
 
AOF rewrite操作就是“压缩”AOF文件的过程,当然redis并没有采用“基于原aof文件”来重写的方式,而是采取了类似snapshot的方式:基于copy-on-write,全量遍历内存中数据,然后逐个序列到aof文件中。因此AOF rewrite能够正确反应当前内存数据的状态,这正是我们所需要的;*rewrite过程中,对于新的变更操作将仍然被写入到原AOF文件中,同时这些新的变更操作也会被redis收集起来(buffer,copy-on-write方式下,最极端的可能是所有的key都在此期间被修改,将会耗费2倍内存),当内存数据被全部写入到新的aof文件之后,收集的新的变更操作也将会一并追加到新的aof文件中,此后将会重命名新的aof文件为appendonly.aof,此后所有的操作都将被写入新的aof文件。如果在rewrite过程中,出现故障,将不会影响原AOF文件的正常工作,只有当rewrite完成之后才会切换文件,因为rewrite过程是比较可靠的。
 
触发rewrite的时机可以通过配置文件来声明,同时redis中可以通过bgrewriteaof指令人工干预。redis-cli -h ip -p port bgrewriteaof
因为rewrite操作/aof记录同步/snapshot都消耗磁盘IO,redis采取了“schedule”策略:无论是“人工干预”还是系统触发,snapshot和rewrite需要逐个被执行。

AOF rewrite过程并不阻塞客户端请求。系统会开启一个子进程来完成。
 三.总结:

AOF和RDB各有优缺点,这是有它们各自的特点所决定:

1) AOF更加安全,可以将数据更加及时的同步到文件中,但是AOF需要较多的磁盘IO开支,AOF文件尺寸较大,文件内容恢复数度相对较慢。 
2) snapshot,安全性较差,它是“正常时期”数据备份以及master-slave数据同步的最佳手段,文件尺寸较小,恢复数度较快。

可以通过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者全部禁用,在架构良好的环境中,master通常使用AOF,slave使用snapshot,主要原因是master需要首先确保数据完整性,它作为数据备份的第一选择;slave提供只读服务(目前slave只能提供读取服务),它的主要目的就是快速响应客户端read请求;但是如果你的redis运行在网络稳定性差/物理环境糟糕情况下,建议你master和slave均采取AOF,这个在master和slave角色切换时,可以减少“人工数据备份”/“人工引导数据恢复”的时间成本;如果你的环境一切非常良好,且服务需要接收密集性的write操作,那么建议master采取snapshot,而slave采用AOF。
 
参考文档:http://www.linuxidc.com/Linux/2016-10/136352.htm
http://www.cnblogs.com/chenpingzhao/p/5158791.html
  查看全部
Redis中数据存储模式有2种
  1. cache-only
  2. persistence


cache-only即只做为“缓存”服务,不持久数据,数据在服务终止后将消失,此模式下也将不存在“数据恢复”的手段,是一种安全性低/效率高/容易扩展的方式;
persistence即为内存中的数据持久备份到磁盘文件,在服务重启后可以恢复,此模式下数据相对安全。

对于persistence持久化存储,Redis提供了两种持久化方法
  1. Redis DataBase(简称RDB)
  2. Append-only file (简称AOF)


除了这两种方法,Redis在早起的版本还存在虚拟内存的方法,现在已经被废弃。
 
一、RDB概述

RDB是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。 
  • 优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能 
  • 缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候


这里说的这个执行数据写入到临时文件的时间点是可以通过配置来自己确定的,通过配置redis在n秒内如果超过m个key被修改这执行一次RDB操作。这个操作就类似于在这个时间点来保存一次Redis的所有数据,一次快照数据。所有这个持久化方法也通常叫做snapshots。

RDB默认开启,redis.conf中的具体配置参数如下:
#dbfilename:持久化数据存储在本地的文件 
dbfilename dump.rdb 
#dir:持久化数据存储在本地的路径,如果是在/redis/redis-3.0.6/src下启动的redis-cli,则数据会存储在当前src目录下 dir ./ 
##snapshot触发的时机,save <seconds> <changes> 
##如下为900秒后,至少有一个变更操作,才会snapshot 
##对于此值的设置,需要谨慎,评估系统的变更操作密集程度 
##可以通过“save “””来关闭snapshot功能 
#save时间,以下分别表示更改了1个key时间隔900s进行持久化存储;更改了10个key300s进行存储;更改10000个key60s进行存储。 
save 900 1 
save 300 10 
save 60 10000 
 
##当snapshot时出现错误无法继续时,是否阻塞客户端“变更操作”,“错误”可能因为磁盘已满/磁盘故障/OS级别异常等 
stop-writes-on-bgsave-error yes 
 
##是否启用rdb文件压缩,默认为“yes”,压缩往往意味着“额外的cpu消耗”,同时也意味这较小的文件尺寸以及较短的网络传输时间 
rdbcompression yes
snapshot触发的时机,是有“间隔时间”和“变更次数”共同决定,同时符合2个条件才会触发snapshot,否则“变更次数”会被继续累加到下一个“间隔时间”上。snapshot过程中并不阻塞客户端请求。snapshot首先将数据写入临时文件,当成功结束后,将临时文件重名为dump.rdb。

使用RDB恢复数据: 
自动的持久化数据存储到dump.rdb后。实际只要重启redis服务即可完成(启动redis的server时会从dump.rdb中先同步数据)

客户端使用命令进行持久化save存储:
./redis-cli -h ip -p port save 
./redis-cli -h ip -p port bgsave

一个是在前台进行存储,一个是在后台进行存储。我的client就在server这台服务器上,所以不需要连其他机器,直接./redis-cli bgsave。由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。 
二、AOF概述

Append-only file,将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾部,在append操作返回后(已经写入到文件或者即将写入),才进行实际的数据变更,“日志文件”保存了历史所有的操作过程;当server需要数据恢复时,可以直接replay此日志文件,即可还原所有的操作过程。AOF相对可靠,它和mysql中bin.log、apache.log、zookeeper中txn-log简直异曲同工。AOF文件内容是字符串,非常容易阅读和解析。 
  • 优点:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志修复;AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)。 
  • 缺点:AOF文件比RDB文件大,且恢复速度慢。


我们可以简单的认为AOF就是日志文件,此文件只会记录“变更操作”(例如:set/del等),如果server中持续的大量变更操作,将会导致AOF文件非常的庞大,意味着server失效后,数据恢复的过程将会很长;事实上,一条数据经过多次变更,将会产生多条AOF记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的;因为AOF持久化模式还伴生了“AOF rewrite”。 
 
AOF的特性决定了它相对比较安全,如果你期望数据更少的丢失,那么可以采用AOF模式。如果AOF文件正在被写入时突然server失效,有可能导致文件的最后一次记录是不完整,你可以通过手工或者程序的方式去检测并修正不完整的记录,以便通过aof文件恢复能够正常;同时需要提醒,如果你的redis持久化手段中有aof,那么在server故障失效后再次启动前,需要检测aof文件的完整性。

AOF默认关闭,开启方法,修改配置文件reds.conf:appendonly yes
##此选项为aof功能的开关,默认为“no”,可以通过“yes”来开启aof功能 
##只有在“yes”下,aof重写/文件同步等特性才会生效 appendonly yes 
##指定aof文件名称 appendfilename appendonly.aof 
##指定aof操作中文件同步策略,有三个合法值:always everysec no,默认为everysec
 appendfsync everysec
##在aof-rewrite期间,appendfsync是否暂缓文件同步,"no"表示“不暂缓”,“yes”表示“暂缓”,默认为“no”
 no-appendfsync-on-rewrite no 
##aof文件rewrite触发的最小文件尺寸(mb,gb),只有大于此aof文件大于此尺寸是才会触发rewrite,默认“64mb”,建议“512mb”
 auto-aof-rewrite-min-size 64mb 
##相对于“上一次”rewrite,本次rewrite触发时aof文件应该增长的百分比。 
##每一次rewrite之后,redis都会记录下此时“新aof”文件的大小(例如A),那么当aof文件增长到A*(1 + p)之后 
##触发下一次rewrite,每一次aof记录的添加,都会检测当前aof文件的尺寸。 
auto-aof-rewrite-percentage 100
 
AOF是文件操作,对于变更操作比较密集的server,那么必将造成磁盘IO的负荷加重;此外linux对文件操作采取了“延迟写入”手段,即并非每次write操作都会触发实际磁盘操作,而是进入了buffer中,当buffer数据达到阀值时触发实际写入(也有其他时机),这是linux对文件系统的优化,但是这却有可能带来隐患,如果buffer没有刷新到磁盘,此时物理机器失效(比如断电),那么有可能导致最后一条或者多条aof记录的丢失。通过上述配置文件,可以得知redis提供了3中aof记录同步选项:
  1. always:每一条aof记录都立即同步到文件,这是最安全的方式,也以为更多的磁盘操作和阻塞延迟,是IO开支较大。
  2. everysec:每秒同步一次,性能和安全都比较中庸的方式,也是redis推荐的方式。如果遇到物理服务器故障,有可能导致最近一秒内aof记录丢失(可能为部分丢失)。
  3. no:redis并不直接调用文件同步,而是交给操作系统来处理,操作系统可以根据buffer填充情况/通道空闲时间等择机触发同步;这是一种普通的文件操作方式。性能较好,在物理服务器故障时,数据丢失量会因OS配置有关。

 
其实,我们可以选择的太少,everysec是最佳的选择。如果你非常在意每个数据都极其可靠,建议你选择一款“关系性数据库”吧。 
AOF文件会不断增大,它的大小直接影响“故障恢复”的时间,而且AOF文件中历史操作是可以丢弃的。
 
AOF rewrite操作就是“压缩”AOF文件的过程,当然redis并没有采用“基于原aof文件”来重写的方式,而是采取了类似snapshot的方式:基于copy-on-write,全量遍历内存中数据,然后逐个序列到aof文件中。因此AOF rewrite能够正确反应当前内存数据的状态,这正是我们所需要的;*rewrite过程中,对于新的变更操作将仍然被写入到原AOF文件中,同时这些新的变更操作也会被redis收集起来(buffer,copy-on-write方式下,最极端的可能是所有的key都在此期间被修改,将会耗费2倍内存),当内存数据被全部写入到新的aof文件之后,收集的新的变更操作也将会一并追加到新的aof文件中,此后将会重命名新的aof文件为appendonly.aof,此后所有的操作都将被写入新的aof文件。如果在rewrite过程中,出现故障,将不会影响原AOF文件的正常工作,只有当rewrite完成之后才会切换文件,因为rewrite过程是比较可靠的。
 
触发rewrite的时机可以通过配置文件来声明,同时redis中可以通过bgrewriteaof指令人工干预。
redis-cli -h ip -p port bgrewriteaof

因为rewrite操作/aof记录同步/snapshot都消耗磁盘IO,redis采取了“schedule”策略:无论是“人工干预”还是系统触发,snapshot和rewrite需要逐个被执行。

AOF rewrite过程并不阻塞客户端请求。系统会开启一个子进程来完成。
 三.总结:

AOF和RDB各有优缺点,这是有它们各自的特点所决定:

1) AOF更加安全,可以将数据更加及时的同步到文件中,但是AOF需要较多的磁盘IO开支,AOF文件尺寸较大,文件内容恢复数度相对较慢。 
2) snapshot,安全性较差,它是“正常时期”数据备份以及master-slave数据同步的最佳手段,文件尺寸较小,恢复数度较快。

可以通过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者全部禁用,在架构良好的环境中,master通常使用AOF,slave使用snapshot,主要原因是master需要首先确保数据完整性,它作为数据备份的第一选择;slave提供只读服务(目前slave只能提供读取服务),它的主要目的就是快速响应客户端read请求;但是如果你的redis运行在网络稳定性差/物理环境糟糕情况下,建议你master和slave均采取AOF,这个在master和slave角色切换时,可以减少“人工数据备份”/“人工引导数据恢复”的时间成本;如果你的环境一切非常良好,且服务需要接收密集性的write操作,那么建议master采取snapshot,而slave采用AOF。
 
参考文档:http://www.linuxidc.com/Linux/2016-10/136352.htm
http://www.cnblogs.com/chenpingzhao/p/5158791.html
 

机械键盘里不同颜色的轴是什么意思?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 5658 次浏览 • 2017-08-28 11:02 • 来自相关话题

Composer在实践中有哪些优化方法和建议?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 4875 次浏览 • 2017-07-05 09:37 • 来自相关话题