竹磬网-邵珠庆の日记 生命只有一次,你可以用它来做些更多伟大的事情–Make the world a little better and easier


98月/220

Linux系统里面图形接口服务器X server

发布在 邵珠庆

X server是Linux系统里面图形接口服务器的简称。Windows系统的界面是这个系统不可分割的一部分,各种窗口操作界面显示都是由系统核心直接管理的,而Linux的图形界面并不是系统的必要组成部分,它可以在无界面的条件下运行。当需要Linux提供界面的时候,系统就会建立一个或者数个X server,通过X协议跟窗口管理器交互,由独立于系统的应用程序来产生窗口,状态栏,按钮之类的交互界面。

比较常见的Linux界面操作环境有KDE和GNOME,为它们提供系统支持的就是X server,而并非Linux核心。

总结一下linux图形界面层次关系:

linux本身-->X服务器<-[通过X协议交谈]->窗口管理器(综合桌面环境)-->X应用程序。

介绍两种方法在命令行中打开远程端的图形应用程序。

两台主机A和B(B是linux主机)

1. A是linux

1)在A主机上,打开终端,执行:ssh -X user@B(ssh -X user@ip)

2)然后在A终端上执行B主机上的图形化界面程序,该图形界面可在A主机显示。

2. A是Windows

需要安装支持x server协议的终端工具

2.1 使用MobaXterm(已经集成x server协议)

1)在A主机上,打开MobaXterm,执行:ssh -X user@B(ssh -X user@ip)

2)然后在MobaXterm上执行B主机上的图形化界面程序,该图形界面可在A主机显示。

2.2 xshell

需要安装xmanager

实测MobaXterm的图形响应速度比xmanager要快,推荐MobaXterm。
————————————————
版权声明:本文为CSDN博主「hello_courage」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012247418/article/details/105347383/

58月/190

linux 系统中 /bin 目录的区别

发布在 邵珠庆

/bin
This directory contains executable programs which are needed in single user mode and to bring the sys‐ tem up or repair it.

此目录包含在单用户模式下需要的可执行程序,这些程序用于启动或修复系统。

/sbin
Like /bin, this directory holds commands needed to boot the system, but which are usually not executed by normal users.

与/bin一样,这个目录包含引导系统所需的命令,但这些命令通常不会由普通用户执行。

/usr/bin
This is the primary directory for executable programs. Most programs executed by normal users which are not needed for booting or for repairing the system and which are not installed locally should be placed in this directory.

主目录是可执行程序。大多数由普通用户执行的、不需要启动或修复系统的程序以及不在本地安装的程序都应该放在这个目录中。

/usr/sbin
This directory contains program binaries for system administration which are not essential for the boot process, for mounting /usr, or for system repair.

此目录包含用于系统管理的程序二进制文件,这些文件对于引导过程、装入/usr或系统修复都不是必需的。

/usr/local/bin
Binaries for programs local to the site.

站点本地程序的二进制文件。

/usr/local/sbin
Locally installed programs for system administration.

本地安装的系统管理程序。

1810月/181

RedisTemplate访问Redis数据结构

发布在 邵珠庆

Redis五种基本数据结构
redis提供键值对的形式对数据进行存储。支持五种数据类型:String(字符串),List(链表),Hash(散列),Set(无序集合),ZSet(有序集合)。下面是网上对其数据结构简单的归纳比较好的,如下:

结构类型 结构存储的值 结构的读写能力
String 可以是字符串、整数或者浮点数 对整个字符串或者字符串的其中一部分执行操作;对象和浮点数执行 自增(increment)或者自减(decrement)
List 一个链表,链表上的每个节点都包含了一个字符串 从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单个或者多个元素;根据值来查找或者移除元素
Hash 包含键值对的无序散列表 添加、获取、移除单个键值对;获取所有键值对
Set 包含字符串的无序收集器(unorderedcollection),并且被包含的每个字符串都是独一无二的、各不相同 添加、获取、移除单个元素;检查一个元素是否存在于某个集合中;计算交集、并集、差集;从集合里卖弄随机获取元素
ZSet 字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定 添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素
Spring-data-Redis简介
对于JAVA语言,我们之前使用Jedis对redis进行基本的指令操作,随着Spring对Jedis进行了很好的封装之后,使用Spring-data-redis包对redis的操作变得更加简单和方便。而Spring-data-Redis则是通过RedisTemplate对象来操作Redis的五种数据结构。

如何引入Spring-data-Redis
1.导入jar包:spring-data-redis-1.8.7.RELEASE.jar 和 jedis-2.9.0.jar
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.7.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>

2.配置文件(SpringBoot方式暂不介绍)
redis.properties

redis.host=192.168.132.128
redis.port=10000
redis.password=123456

redis.minIdle=50
redis.maxIdle=300
redis.maxActive=600
redis.maxWait=1000
redis.testOnBorrow=true

applicationContext.xml

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<!-- 引入小配置文件-->
<value>classpath:redis.properties</value>
</list>
</property>
</bean>

<!-- 连接池 ,本质是对GenericObjectPoolConfig的属性的设置-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="minIdle" value="${redis.minIdle}" />
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxTotal" value="${redis.maxActive}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

<!-- REDIS连接工厂 -->
<bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!-- 基础连接参数 -->
<property name="hostName" value="${redis.host}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.password}" />
<!-- 是否启用连接池 -->
<property name="usePool" value="true" />
<property name="poolConfig" ref="poolConfig" />
</bean>

<!-- 对String类型处理的RedisTemplate -->
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory" />
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>

<!-- 对LIST,SET,ZSET,HASH等类型的处理RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnFactory"/>
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<!-- 对象序列化方案 -->
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
</property>
</bean>

3.注意
推荐使用GenericJackson2JsonRedisSerializer,而不是Jackson2JsonRedisSerializer,因为GenericJackson2JsonRedisSerializer提供了很好的对泛型的支持,而使用Jackson2JsonRedisSerializer对不同对象进行操作时都需要手动set序列化方案,不能直接集成到配置文件中将其直接托管给spring工厂。当然,我们可以自定义序列化方案,同时也可以使用spring-data-redis集成好的序列化方案,例如集成号称速度最快的fastjson序列化方案,下面提供一个fastjson的Serializer(暂时没有集成对泛型纳入工厂方案的支持)。

package util;

import java.nio.charset.Charset;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;

/**
* FASTJSON序列化工具
* @author LiuChengxiang
* @time 2017年9月19日上午9:30:27
*
* @param <T>
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {

public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

private Class<T> clazz;

public FastJson2JsonRedisSerializer(Class<T> clazz){
super();
this.clazz = clazz;
}

@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}

@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T)JSON.parseObject(str,clazz);
}

}

RedisTemplate访问Redis数据结构(前言)

RedisTemplate访问Redis数据结构(一)——String
RedisTemplate访问Redis数据结构(二)——List
RedisTemplate访问Redis数据结构(三)——Hash
RedisTemplate访问Redis数据结构(四)——Set
RedisTemplate访问Redis数据结构(五)——ZSet

https://blog.csdn.net/weixin_37490221/article/details/78134105

98月/180

H5计算器实现

发布在 邵珠庆

<!DOCTYPE html>
<html>
<meta name="content-type" content="text/html; charset=UTF-8">
<head>
<title>Calculator</title>
<!--将按键内容以字符串形式存储在文字框中当按钮为“=”时,调用eval方法计算结果然后将结果输出文字框中-->
<script type="text/javascript">
var numresult;
var str;
function onclicknum(nums) {
str = document.getElementById("nummessege");
str.value = str.value + nums;
}
function onclickclear() {
str = document.getElementById("nummessege");
str.value = "";
}
function onclickresult() {
str = document.getElementById("nummessege");
numresult = eval(str.value);
str.value = numresult;
}
</script>
</head>
<body bgcolor="affff" >
<!--定义按键表格,每个按键对应一个事件触发-->
<table border="1" align="center" bgColor="#bbff77"
style="height: 350px; width: 270px">
<tr>
<td colspan="4">
<input type="text" id="nummessege"
style="height: 90px; width: 350px; font-size: 50px" />
</td>
</tr>
<tr>
<td>
<input type="button" value="1" id="1" onclick="onclicknum(1)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="2" id="2" onclick="onclicknum(2)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="3" id="3" onclick="onclicknum(3)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="+" id="add" onclick="onclicknum('+')"
style="height: 70px; width: 90px; font-size: 35px">
</td>
</tr>
<tr>
<td>
<input type="button" value="4" id="4" onclick="onclicknum(4)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="5" id="5" onclick="onclicknum(5)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="6" id="6" onclick="onclicknum(6)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="-" id="sub" onclick="onclicknum('-')"
style="height: 70px; width: 90px; font-size: 35px">
</td>
</tr>
<tr>
<td>
<input type="button" value="7" id="7" onclick="onclicknum(7)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="8" id="8" onclick="onclicknum(8)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="9" id="9" onclick="onclicknum(9)"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="*" id="mul" onclick="onclicknum('*')"
style="height: 70px; width: 90px; font-size: 35px">
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="0" id="0" onclick="onclicknum(0)"
style="height: 70px; width: 190px; font-size: 35px">
</td>
<td>
<input type="button" value="." id="point" onclick="onclicknum('.')"
style="height: 70px; width: 90px; font-size: 35px">
</td>
<td>
<input type="button" value="/" id="division"
onclick="onclicknum('/')"
style="height: 70px; width: 90px; font-size: 35px">
</td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="Del" id="clear"
onclick="onclickclear()"
style="height: 70px; width: 190px; font-size: 35px" />
</td>
<td colspan="2">
<input type="button" value="=" id="result"
onclick="onclickresult()"
style="height: 70px; width: 190px; font-size: 35px" />
</td>
</tr>
</table>
</body>
</html>
65月/180

Redis内存分析方法

发布在 邵珠庆

一般会采用 bgsave 生成 dump.rdb 文件,再结合 redis-rdb-tools 和 sqlite 来进行静态分析。

BGSAVE:在后台异步(Asynchronously)保存当前数据库的数据到磁盘。

BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

生成内存快照:redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在分析内存的时候,主要用它生成内存快照。

redis-rdb-tools 安装:

使用 PYPI 安装:

pip install rdbtools

使用 源码安装:

git clone https://github.com/sripathikrishnan/redis-rdb-tools
cd redis-rdb-tools
sudo python setup.py install

使用 redis-rdb-tools 生成内存快照:

rdb -c memory dump.rdb > memory.csv

生成 CSV 格式的内存报告。包含的列有:数据库 ID,数据类型,key,内存使用量(byte),编码。内存使用量包含 key、value 和其他值。

内存使用量是理论上的近似值,在一般情况下,略低于实际值。

[ares:~/Desktop$head memory.csv
database,type,key,size_in_bytes,encoding,num_elements,len_largest_element
0,string,trade.coupon.id:653601465,112,string,8,8
0,string,trade.coupon.id:631354838,112,string,8,8
0,string,trade.coupon.id:632477800,112,string,8,8
0,string,trade.coupon.id:620802294,112,string,8,8
0,string,trade.coupon.id:631432959,112,string,8,8
0,string,trade.coupon.id:632933399,112,string,8,8
0,string,trade.coupon.id:632117725,112,string,8,8
0,string,trade.coupon.id:634240609,112,string,8,8
0,string,trade.coupon.id:646317603,112,string,8,8

注:若csv文件不大,可直接用相关软件打开,以size_in_bytes列排序,可以看到大致内存使用。

使用SQLite分析内存快照:

SQLite版本必须是3.16.0以上。

导入memory.csv数据库:

$sqlite3 memory.db
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> create table memory(database int,type varchar(128),key varchar(128),size_in_bytes int,encoding varchar(128),num_elements int,len_largest_element varchar(128));
sqlite> .mode csv memory
sqlite> .import memory.csv memory

数据导入后,可以随处理:

查询key总数:

sqlite> select count(*) from memory;
31143847

查询key总占用内存:

sqlite> select sum(size_in_bytes) from memory;
17391950414.0

查询内容占用最高的几个key:

sqlite> select key,size_in_bytes from memory order by size_in_bytes desc limit 10;
key,size_in_bytes
public.xx.xx:xx,7860169636
public.xx.xx:xx,3043206524
public.xx.xx:xx,1866022916
public.xx.xx:xx,420931316
public.xx.xx:idxx171118172
xx,162984940
xx,133443892
public.xx.xx:xx,80925132
public.xx.xx:xx,28340356
63月/180

Vagrant-安装教程及常见问题

发布在 邵珠庆

前言:
Vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境。
它的主要意义是让所有开发人员都使用和线上服务器一样的环境,本质上和你新建一个虚拟机。
那最常见的,正常我们是怎么开发呢,大部分童鞋应该是在windows下搭建开发环境,敲代码,运行程序,达到效果git或svn提交,发布linux环境再看效果。
而vagrant virtualBox实现了把代码同步共享到linux虚拟机,而这个虚拟机你可以配成和你生产环境一样的,
说白了,通过共享文件到虚拟机,在类生产环境下运行。【让你在windows下体验到在linux开发的效果。
还有一种共享方式,可以借助IDE的develop功能通过sftp上传到服务器,然后访问服务器
三种方式总结:
【1】原始windows开发模式:windows开发-本地访问调试(与生产环境毕竟不同)-发布到linux运行
【2】vagrant virtualBox模式: windows开发 - 文件本地共享 -- 直接访问虚拟机(linux环境与生产一致)
【3】IDE develop 模式: windows开发 - 文件远程上传 -- 访问远程开发机(linux环境与生产一致)
其实【1】,与【2,3】的区别就在于,程序是在哪里运行的,windows本地?or Linux仿生产环境
(能在仿生产的环境直接开发肯定比在windows开发在放到linux更好些,开发方便避免一些环境上等的麻烦)
【2】与【3】的区别就在于文件是如何同步的:【2】是通过虚拟机文件共享实现同步;【3】直接利用sftp远程上传实现同步。
缺点:
【1】毫无疑问,它的弊端就是开发时不能模拟生产环境,可能会有衔接问题,环境有了问题不像虚拟机重安一台立刻搞定。
【3】存在的问题,比如:当切换开发分支后改动了文件a和b,当前ide选中的是a文件,ok他会自动上传更新,但是b文件不会,因为窗口你没在b文件下呀,没有那么智能不会自动触发上传更新,这点就坑了造成代码不同步,需要你自己手动触发一下相关文件的上传,尤其是依赖一些包的时候会发生丢失,得全项目上传一次;
【2】因为是本地和虚拟机的文件共享嘛,没有文件上传遗漏一说,所以还是很推荐用
目录:
一。安装虚拟机
二。Vagrantfile配置文件详解
三。连接虚拟机
四。碰到问题

一。安装虚拟机
1.添加box
vagrant box add base your_box_addres
注意:base是默认名称,主要用来标识一下你添加的box,后面的命令都是基于这个标识来操作的,你也可以用其他名称【但是用了其他名字记得在第二步用此名字init】
2.初始化
vagrant init
vagrant init box_name
如果你添加的box名称不是[base],那么需要在初始化的时候指定名称
3.启动虚拟机
vagrant up
启动过程可能比较长,耐心等待
图一.运行过程总图
图二
相关指令:

[plain] view plain copy

  1. vagrant up (启动虚拟机)
  2. vagrant halt (关闭虚拟机——对应就是关机)
  3. vagrant suspend (暂停虚拟机——只是暂停,虚拟机内存等信息将以状态文件的方式保存在本地,可以执行恢复操作后继续使用)
  4. vagrant resume (恢复虚拟机—— 与前面的暂停相对应)
  5. vagrant destroy (删除虚拟机,删除后在当前虚拟机所做进行的除开Vagrantfile中的配置都不会保留)
二.Vagrantfile配置文件详解
在我们的开发目录下有一个文件Vagrantfile,里面包含有大量的配置信息,主要包括三个方面的配置,虚拟机的配置、SSH配置、Vagrant的一些基础配置。(它的配置语法也是Ruby的)【修改配置文件,记得完后重启vagrant才能生效哦】

在我们的开发目录下有一个文件Vagrantfile,里面包含有大量的配置信息,主要包括三个方面的配置,虚拟机的配置、SSH配置、Vagrant的一些基础配置。Vagrant是使用Ruby开发的,所以它的配置语法也是Ruby的,但是我们没有学过Ruby的人还是可以跟着它的注释知道怎么配置一些基本项的配置。

具体介绍,参考:http://blog.csdn.net/chajinglong/article/details/52805915

[plain] view plain copy

  1. # -*- mode: ruby -*-
  2. # vi: set ft=ruby :
  3. # All Vagrant configuration is done below. The "2" in Vagrant.configure
  4. # configures the configuration version (we support older styles for
  5. # backwards compatibility). Please don\'t change it unless you know what
  6. # you\'re doing.
  7. Vagrant.configure(2) do |config|
  8.   # The most common configuration options are documented and commented below.
  9.   # For a complete reference, please see the online documentation at
  10.   # https://docs.vagrantup.com.
  11.   # Every Vagrant development environment requires a box. You can search for
  12.   # boxes at https://atlas.hashicorp.com/search.
  13.   config.vm.box = "base"
  14.   # Disable automatic box update checking. If you disable this, then
  15.   # boxes will only be checked for updates when the user runs
  16.   # `vagrant box outdated`. This is not recommended.
  17.   # config.vm.box_check_update = false
  18.   # Create a forwarded port mapping which allows access to a specific port
  19.   # within the machine from a port on the host machine. In the example below,
  20.   # accessing "localhost:8080" will access port 80 on the guest machine.
  21.   # config.vm.network "forwarded_port", guest: 80, host: 80
  22.   # Create a private network, which allows host-only access to the machine
  23.   # using a specific IP.
  24.   config.vm.network "private_network", ip: "192.168.33.10"
  25.   # Create a public network, which generally matched to bridged network.
  26.   # Bridged networks make the machine appear as another physical device on
  27.   # your network.
  28.   # config.vm.network "public_network"
  29.   # Share an additional folder to the guest VM. The first argument is
  30.   # the path on the host to the actual folder. The second argument is
  31.   # the path on the guest to mount the folder. And the optional third
  32.   # argument is a set of non-required options.
  33.   config.vm.synced_folder "D:/all_code/", "/home/www"
  34.   # Provider-specific configuration so you can fine-tune various
  35.   # backing providers for Vagrant. These expose provider-specific options.
  36.   # Example for VirtualBox:
  37.   #
  38.   # config.vm.provider "virtualbox" do |vb|
  39.   #   # Display the VirtualBox GUI when booting the machine
  40.   #   vb.gui = true
  41.   #
  42.   #   # Customize the amount of memory on the VM:
  43.   #   vb.memory = "1024"
  44.   # end
  45.   #
  46.   # View the documentation for the provider you are using for more
  47.   # information on available options.
  48.   # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  49.   # such as FTP and Heroku are also available. See the documentation at
  50.   # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  51.   # config.push.define "atlas" do |push|
  52.   #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  53.   # end
  54.   # Enable provisioning with a shell script. Additional provisioners such as
  55.   # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  56.   # documentation for more information about their specific syntax and use.
  57.   # config.vm.provision "shell", inline: <
  58.   #   sudo apt-get update
  59.   #   sudo apt-get install -y apache2
  60.   # SHELL
  61. end
三.连接虚拟机
1.虚拟机相关登录信息
vagrant ssh
这样我们就可以像连接到一台服务器一样进行操作了。
图三
2.ssh登录
window机器不支持这样的命令,必须使用第三方客户端来进行连接,例如xmoba、putty、Xshell等.

[plain] view plain copy

  1. ssh: 127.0.0.1
  2. 端口: 2222
  3. 用户名: vagrant
  4. 密码: vagrant
图四.xshell登陆举例
图五.xmoba登陆举例
3.系统信息
df -h
/vagrant这个目录是自动映射的,被映射到你刚刚建立的文件夹,这样就方便我们以后在开发机中进行开发,在虚拟机中进行运行效果测试了。
四。碰到问题:
1.vagrant up 时提示错误 cound not open file 问题
如果init指定了 add的名称test,那么init时也要说明,少了这一步
如下边错误演示与正确演示。
图六.错误演示
图七.正确演示
2.vagrant up 时提示错误:

[plain] view plain copy

  1. The guest machine entered an invalid state while waiting for it
  2. to boot. Valid states are \'starting, running\'. The machine is in the
  3. \'poweroff\' state. Please verify everything is configured
  4. properly and try again.
  5. If the provider you\'re using has a GUI that comes with it,
  6. it is often helpful to open that and watch the machine, since the
  7. GUI often has more helpful error messages than Vagrant can retrieve.
  8. For example, if you\'re using VirtualBox, run `vagrant up` while the
  9. VirtualBox GUI is open.
  10. The primary issue for this error is that the provider you\'re using
  11. is not properly configured. This is very rarely a Vagrant issue.
打开虚拟机启动,也会提示报错:

[plain] view plain copy

  1. Unable to load R3 module D:\virtualBox/VBoxDD.DLL (VBoxDD): GetLastError=1790 (VERR_UNRESOLVED_ERROR).
原因:
本目录中的以下三个文件是原始的未被破解的WIN7 64位系统主题文件:
themeservice.dll , themeui.dll ,uxtheme.dll
为了使用工具UniversalThemePatcher-x64.exe恢复主题,特地将以上3个文件,
各拷贝了一份,并重新命名如下:
themeservice.dll.backup ,themeui.dll.backup ,uxtheme.dll.backup
解决方法:
我们只须把重命名后的3个文件:
themeservice.dll.backup ,themeui.dll.backup ,uxtheme.dll.backup
拷贝到C:\Windows\System32目录下面,
然后运行工具UniversalThemePatcher-x64.exe进行恢复即可。
下载地址:http://download.csdn.net/download/ty_hf/10013443【里边有使用说明】
3.虚拟机一切正常,文件代码也映射到虚拟机,但就是不能正常访问。
a).配置文件需要删除

[plain] view plain copy

  1. sudo rm -f /etc/udev/rules.d/70-persistent-net.rules
问题就处在在持久网络设备udev规则(persistent network device udev rules)是被原VM设置好的,再用box生成新VM时,这些rules需要被更新。而这和Vagrantfile里对新VM设置private network的指令发生冲突。删除就好了。
b).如果设置了host,虚拟机里也要设置一次

[plain] view plain copy

  1. vi /etc/hosts

3.问题 default: Warning: Authentication failure. Retrying...

[plain] view plain copy

  1. Bringing machine \'default\' up with \'virtualbox\' provider...
  2. ==> default: Clearing any previously set forwarded ports...
  3. ==> default: Clearing any previously set network interfaces...
  4. ==> default: Preparing network interfaces based on configuration...
  5.     default: Adapter 1: nat
  6.     default: Adapter 2: hostonly
  7. ==> default: Forwarding ports...
  8.     default: 22 (guest) => 2222 (host) (adapter 1)
  9. ==> default: Booting VM...
  10. ==> default: Waiting for machine to boot. This may take a few minute
  11.     default: SSH address: 127.0.0.1:2222
  12.     default: SSH username: vagrant
  13.     default: SSH auth method: private key
  14.     default: Warning: Remote connection disconnect. Retrying...
  15.     default: Warning: Authentication failure. Retrying...
  16.     default: Warning: Authentication failure. Retrying...
  17.     default: Warning: Authentication failure. Retrying...
  18. Timed out while waiting for the machine to boot. This means that
  19. Vagrant was unable to communicate with the guest machine within
  20. the configured ("config.vm.boot_timeout" value) time period.
  21. If you look above, you should be able to see the error(s) that
  22. Vagrant had when attempting to connect to the machine. These errors
  23. are usually good hints as to what may be wrong.
  24. If you\'re using a custom box, make sure that networking is properly
  25. working and you\'re able to connect to the machine. It is a common
  26. problem that networking isn\'t setup properly in these boxes.
  27. Verify that authentication configurations are also setup properly,
  28. as well.
  29. If the box appears to be booting properly, you may want to increase
  30. the timeout ("config.vm.boot_timeout") value.

解决:

编辑Vagrantfile ,添加如下文件

[plain] view plain copy

  1. config.ssh.username = "vagrant"
  2. config.ssh.password = "vagrant"
然后重启vagrant 重新加载配置文件

[plain] view plain copy

  1. vagrant halt
  2. vagrant up

注意登陆的时候看下 vagrant ssh 看下你的登录信息,端口号

4.报错问题:

[plain] view plain copy

  1. Bringing machine \'default\' up with \'virtualbox\' provider...
  2. Your VM has become "inaccessible." Unfortunately, this is a critical error
  3. with VirtualBox that Vagrant can not cleanly recover from. Please open VirtualBox
  4. and clear out your inaccessible virtual machines or find a way to fix
 解决:
 http://doodlebobbers.com/vagrant-error-your-vm-has-become-inaccessible/
其实就是删除.vagrant文件夹,然后重启vagrant
5.vagrant 启动的时候报错

[plain] view plain copy

  1. A Vagrant environment or target machine is required to run this
  2. command. Run vagrant init to create a new Vagrant environment. Or,
  3. get an ID of a target machine from vagrant global-status to run
  4. this command on. A final option is to change to a directory with a
  5. Vagrantfile and to try again.

少前边步骤了,比如vagrant init;或者没有进入对应vagrant init的文件夹

6.vagrant dns解析异常,访问接口特别慢,在Vagrantfile 配置文件中添加下边代码:

[plain] view plain copy

  1. config.vm.provider :virtualbox do |vb|
  2. vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
  3. vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
7.vagrant up 又提示新报错!

[plain] view plain copy

  1. D:\vcode>vagrant up
  2. Bringing machine \'default\' up with \'virtualbox\' provider...
  3. Your VM has become "inaccessible." Unfortunately, this is a critical error
  4. with VirtualBox that Vagrant can not cleanly recover from. Please open VirtualBo
  5. x
  6. and clear out your inaccessible virtual machines or find a way to fix
  7. them.
相关资源:
官网下载:https://www.vagrantup.com/downloads.html
虚拟机等相关安装包: 链接:http://pan.baidu.com/s/1dEWSnEL 密码:ajaq
本文地址:http://blog.csdn.net/ty_hf/article/details/78314583
301月/180

Token 认证的来龙去脉

发布在 邵珠庆

为什么要用 Token?

而要回答这个问题很简单——因为它能解决问题! 可以解决哪些问题呢?

  1. Token 完全由应用管理,所以它可以避开同源策略
  2. Token 可以避免 CSRF 攻击
  3. Token 可以是无状态的,可以在多个服务间共享

Token 是在服务端产生的。如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 Token 证明自己的合法地位。如果这个 Token 在服务端持久化(比如存入数据库),那它就是一个永久的身份令牌。 于是,又一个问题产生了:需要为 Token 设置有效期吗?

需要设置有效期吗?

对于这个问题,我们不妨先看两个例子。一个例子是登录密码,一般要求定期改变密码,以防止泄漏,所以密码是有有效期的;另一个例子是安全证书。SSL 安全证书都有有效期,目的是为了解决吊销的问题,对于这个问题的详细情况,来看看知乎的回答。所以无论是从安全的角度考虑,还是从吊销的角度考虑,Token 都需要设有效期。 那么有效期多长合适呢? 只能说,根据系统的安全需要,尽可能的短,但也不能短得离谱——想像一下手机的自动熄屏时间,如果设置为 10 秒钟无操作自动熄屏,再次点亮需要输入密码,会不会疯?如果你觉得不会,那就亲自试一试,设置成可以设置的最短时间,坚持一周就好(不排除有人适应这个时间,毕竟手机厂商也是有用户体验研究的)。 然后新问题产生了,如果用户在正常操作的过程中,Token 过期失效了,要求用户重新登录……用户体验岂不是很糟糕? 为了解决在操作过程不能让用户感到 Token 失效这个问题,有一种方案是在服务器端保存 Token 状态,用户每次操作都会自动刷新(推迟) Token 的过期时间——Session 就是采用这种策略来保持用户登录状态的。然而仍然存在这样一个问题,在前后端分离、单页 App 这些情况下,每秒种可能发起很多次请求,每次都去刷新过期时间会产生非常大的代价。如果 Token 的过期时间被持久化到数据库或文件,代价就更大了。所以通常为了提升效率,减少消耗,会把 Token 的过期时保存在缓存或者内存中。 还有另一种方案,使用 Refresh Token,它可以避免频繁的读写操作。这种方案中,服务端不需要刷新 Token 的过期时间,一旦 Token 过期,就反馈给前端,前端使用 Refresh Token 申请一个全新 Token 继续使用。这种方案中,服务端只需要在客户端请求更新 Token 的时候对 Refresh Token 的有效性进行一次检查,大大减少了更新有效期的操作,也就避免了频繁读写。当然 Refresh Token 也是有有效期的,但是这个有效期就可以长一点了,比如,以天为单位的时间。

时序图表示

使用 Token 和 Refresh Token 的时序图如下:

1)登录

自动草稿

2)业务请求

自动草稿

3)Token 过期,刷新 Token

自动草稿 上面的时序图中并未提到 Refresh Token 过期怎么办。不过很显然,Refresh Token 既然已经过期,就该要求用户重新登录了。 当然还可以把这个机制设计得更复杂一些,比如,Refresh Token 每次使用的时候,都更新它的过期时间,直到与它的创建时间相比,已经超过了非常长的一段时间(比如三个月),这等于是在相当长一段时间内允许 Refresh Token 自动续期。 到目前为止,Token 都是有状态的,即在服务端需要保存并记录相关属性。那说好的无状态呢,怎么实现?

无状态 Token

如果我们把所有状态信息都附加在 Token 上,服务器就可以不保存。但是服务端仍然需要认证 Token 有效。不过只要服务端能确认是自己签发的 Token,而且其信息未被改动过,那就可以认为 Token 有效——“签名”可以作此保证。平时常说的签名都存在一方签发,另一方验证的情况,所以要使用非对称加密算法。但是在这里,签发和验证都是同一方,所以对称加密算法就能达到要求,而对称算法比非对称算法要快得多(可达数十倍差距)。更进一步思考,对称加密算法除了加密,还带有还原加密内容的功能,而这一功能在对 Token 签名时并无必要——既然不需要解密,摘要(散列)算法就会更快。可以指定密码的散列算法,自然是 HMAC。 上面说了这么多,还需要自己去实现吗?不用!JWT 已经定义了详细的规范,而且有各种语言的若干实现。 不过在使用无状态 Token 的时候在服务端会有一些变化,服务端虽然不保存有效的 Token 了,却需要保存未到期却已注销的 Token。如果一个 Token 未到期就被用户主动注销,那么服务器需要保存这个被注销的 Token,以便下次收到使用这个仍在有效期内的 Token 时判其无效。有没有感到一点沮丧? 在前端可控的情况下(比如前端和服务端在同一个项目组内),可以协商:前端一但注销成功,就丢掉本地保存(比如保存在内存、LocalStorage 等)的 Token 和 Refresh Token。基于这样的约定,服务器就可以假设收到的 Token 一定是没注销的(因为注销之后前端就不会再使用了)。 如果前端不可控的情况,仍然可以进行上面的假设,但是这种情况下,需要尽量缩短 Token 的有效期,而且必须在用户主动注销的情况下让 Refresh Token 无效。这个操作存在一定的安全漏洞,因为用户会认为已经注销了,实际上在较短的一段时间内并没有注销。如果应用设计中,这点漏洞并不会造成什么损失,那采用这种策略就是可行的。 在使用无状态 Token 的时候,有两点需要注意:

  1. Refresh Token 有效时间较长,所以它应该在服务器端有状态,以增强安全性,确保用户注销时可控
  2. 应该考虑使用二次认证来增强敏感操作的安全性

到此,关于 Token 的话题似乎差不多了——然而并没有,上面说的只是认证服务和业务服务集成在一起的情况,如果是分离的情况呢?

分离认证服务

当 Token 无状态之后,单点登录就变得容易了。前端拿到一个有效的 Token,它就可以在任何同一体系的服务上认证通过——只要它们使用同样的密钥和算法来认证 Token 的有效性。就样这样: 自动草稿 当然,如果 Token 过期了,前端仍然需要去认证服务更新 Token: 自动草稿 可见,虽然认证和业务分离了,实际即并没产生多大的差异。当然,这是建立在认证服务器信任业务服务器的前提下,因为认证服务器产生 Token 的密钥和业务服务器认证 Token 的密钥和算法相同。换句话说,业务服务器同样可以创建有效的 Token。 如果业务服务器不能被信任,该怎么办?

不受信的业务服务器

遇到不受信的业务服务器时,很容易想到的办法是使用不同的密钥。认证服务器使用密钥1签发,业务服务器使用密钥2验证——这是典型非对称加密签名的应用场景。认证服务器自己使用私钥对 Token 签名,公开公钥。信任这个认证服务器的业务服务器保存公钥,用于验证签名。幸好,JWT 不仅可以使用 HMAC 签名,也可以使用 RSA(一种非对称加密算法)签名。 不过,当业务服务器已经不受信任的时候,多个业务服务器之间使用相同的 Token 对用户来说是不安全的。因为任何一个服务器拿到 Token 都可以仿冒用户去另一个服务器处理业务……悲剧随时可能发生。 为了防止这种情况发生,就需要在认证服务器产生 Token 的时候,把使用该 Token 的业务服务器的信息记录在 Token 中,这样当另一个业务服务器拿到这个 Token 的时候,发现它并不是自己应该验证的 Token,就可以直接拒绝。 现在,认证服务器不信任业务服务器,业务服务器相互也不信任,但前端是信任这些服务器的——如果前端不信任,就不会拿 Token 去请求验证。那么为什么会信任?可能是因为这些是同一家公司或者同一个项目中提供的若干服务构成的服务体系。 但是,前端信任不代表用户信任。如果 Token 不没有携带用户隐私(比如姓名),那么用户不会关心信任问题。但如果 Token 含有用户隐私的时候,用户得关心信任问题了。这时候认证服务就不得不再啰嗦一些,当用户请求 Token 的时候,问上一句,你真的要授权给某某某业务服务吗?而这个“某某某”,用户怎么知道它是不是真的“某某某”呢?用户当然不知道,甚至认证服务也不知道,因为公钥已经公开了,任何一个业务都可以声明自己是“某某某”。 为了得到用户的信任,认证服务就不得不帮助用户来甄别业务服务。所以,认证服器决定不公开公钥,而是要求业务服务先申请注册并通过审核。只有通过审核的业务服务器才能得到认证服务为它创建的,仅供它使用的公钥。如果该业务服务泄漏公钥带来风险,由该业务服务自行承担。现在认证服务可以清楚的告诉用户,“某某某”服务是什么了。如果用户还是不够信任,认证服务甚至可以问,某某某业务服务需要请求 A、B、C 三项个人数据,其中 A 是必须的,不然它不工作,是否允许授权?如果你授权,我就把你授权的几项数据加密放在 Token 中…… 废话了这么多,有没有似曾相识……对了,这类似开放式 API 的认证过程。开发式 API 多采用 OAuth 认证,而关于 OAuth 的探讨资源非常丰富,这里就不深究了。

211月/170

Tutorial: Creating a Simple REST API

发布在 邵珠庆

Tutorial: Creating a Simple REST API

In this tutorial, we will explain how to create a simple application that provides a RESTful API using the different HTTP methods:

  • GET to retrieve and search data
  • POST to add data
  • PUT to update data
  • DELETE to delete data

Defining the API

The API consists of the following methods:

Method URL Action
GET /api/robots Retrieves all robots
GET /api/robots/search/Astro Searches for robots with 'Astro' in their name
GET /api/robots/2 Retrieves robots based on primary key
POST /api/robots Adds a new robot
PUT /api/robots/2 Updates robots based on primary key
DELETE /api/robots/2 Deletes robots based on primary key

Creating the Application

As the application is so simple, we will not implement any full MVC environment to develop it. In this case, we will use a micro application to meet our goal.

The following file structure is more than enough:

my-rest-api/
    models/
        Robots.php
    index.php
    .htaccess

First, we need a .htaccess file that contains all the rules to rewrite the request URIs to the index.php file (application entry-point):

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^((?s).*)$ index.php?_url=/$1 [QSA,L]
</IfModule>

The bulk of our code will be placed in index.php. The file is created as follows:

<?php

use Phalcon\Mvc\Micro;

$app = new Micro();

// Define the routes here

$app->handle();

Now we will create the routes as we defined above:

<?php

use Phalcon\Mvc\Micro;

$app = new Micro();

// Retrieves all robots
$app->get(
    '/api/robots',
    function () {
        // Operation to fetch all the robots
    }
);

// Searches for robots with $name in their name
$app->get(
    '/api/robots/search/{name}',
    function ($name) {
        // Operation to fetch robot with name $name
    }
);

// Retrieves robots based on primary key
$app->get(
    '/api/robots/{id:[0-9]+}',
    function ($id) {
        // Operation to fetch robot with id $id
    }
);

// Adds a new robot
$app->post(
    '/api/robots',
    function () {
        // Operation to create a fresh robot
    }
);

// Updates robots based on primary key
$app->put(
    '/api/robots/{id:[0-9]+}',
    function ($id) {
        // Operation to update a robot with id $id
    }
);

// Deletes robots based on primary key
$app->delete(
    '/api/robots/{id:[0-9]+}',
    function ($id) {
        // Operation to delete the robot with id $id
    }
);

$app->handle();

Each route is defined with a method with the same name as the HTTP method, as first parameter we pass a route pattern, followed by a handler. In this case, the handler is an anonymous function. The following route: /api/robots/{id:[0-9]+}, by example, explicitly sets that the id parameter must have a numeric format.

When a defined route matches the requested URI then the application executes the corresponding handler.

Creating a Model

Our API provides information about robots, these data are stored in a database. The following model allows us to access that table in an object-oriented way. We have implemented some business rules using built-in validators and simple validations. Doing this will give us the peace of mind that saved data meet the requirements of our application. This model file should be placed in your Models folder.

<?php

namespace Store\Toys;

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Message;
use Phalcon\Mvc\Model\Validator\Uniqueness;
use Phalcon\Mvc\Model\Validator\InclusionIn;

class Robots extends Model
{
    public function validation()
    {
        // Type must be: droid, mechanical or virtual
        $this->validate(
            new InclusionIn(
                [
                    'field'  => 'type',
                    'domain' => [
                        'droid',
                        'mechanical',
                        'virtual',
                    ],
                ]
            )
        );

        // Robot name must be unique
        $this->validate(
            new Uniqueness(
                [
                    'field'   => 'name',
                    'message' => 'The robot name must be unique',
                ]
            )
        );

        // Year cannot be less than zero
        if ($this->year < 0) {
            $this->appendMessage(
                new Message('The year cannot be less than zero')
            );
        }

        // Check if any messages have been produced
        if ($this->validationHasFailed() === true) {
            return false;
        }
    }
}

Now, we must set up a connection to be used by this model and load it within our app [File: index.php]:

<?php

use Phalcon\Loader;
use Phalcon\Mvc\Micro;
use Phalcon\Di\FactoryDefault;
use Phalcon\Db\Adapter\Pdo\Mysql as PdoMysql;

// Use Loader() to autoload our model
$loader = new Loader();

$loader->registerNamespaces(
    [
        'Store\Toys' => __DIR__ . '/models/',
    ]
);

$loader->register();

$di = new FactoryDefault();

// Set up the database service
$di->set(
    'db',
    function () {
        return new PdoMysql(
            [
                'host'     => 'localhost',
                'username' => 'asimov',
                'password' => 'zeroth',
                'dbname'   => 'robotics',
            ]
        );
    }
);

// Create and bind the DI to the application
$app = new Micro($di);

Retrieving Data

The first handler that we will implement is which by method GET returns all available robots. Let's use PHQL to perform this simple query returning the results as JSON. [File: index.php]

<?php

// Retrieves all robots
$app->get(
    '/api/robots',
    function () use ($app) {
        $phql = 'SELECT * FROM Store\Toys\Robots ORDER BY name';

        $robots = $app->modelsManager->executeQuery($phql);

        $data = [];

        foreach ($robots as $robot) {
            $data[] = [
                'id'   => $robot->id,
                'name' => $robot->name,
            ];
        }

        echo json_encode($data);
    }
);

PHQL, allow us to write queries using a high-level, object-oriented SQL dialect that internally translates to the right SQL statements depending on the database system we are using. The clause use in the anonymous function allows us to pass some variables from the global to local scope easily.

The searching by name handler would look like [File: index.php]:

<?php

// Searches for robots with $name in their name
$app->get(
    '/api/robots/search/{name}',
    function ($name) use ($app) {
        $phql = 'SELECT * FROM Store\Toys\Robots WHERE name LIKE :name: ORDER BY name';

        $robots = $app->modelsManager->executeQuery(
            $phql,
            [
                'name' => '%' . $name . '%'
            ]
        );

        $data = [];

        foreach ($robots as $robot) {
            $data[] = [
                'id'   => $robot->id,
                'name' => $robot->name,
            ];
        }

        echo json_encode($data);
    }
);

Searching by the field id it's quite similar, in this case, we're also notifying if the robot was found or not [File: index.php]:

<?php

use Phalcon\Http\Response;

// Retrieves robots based on primary key
$app->get(
    '/api/robots/{id:[0-9]+}',
    function ($id) use ($app) {
        $phql = 'SELECT * FROM Store\Toys\Robots WHERE id = :id:';

        $robot = $app->modelsManager->executeQuery(
            $phql,
            [
                'id' => $id,
            ]
        )->getFirst();

        // Create a response
        $response = new Response();

        if ($robot === false) {
            $response->setJsonContent(
                [
                    'status' => 'NOT-FOUND'
                ]
            );
        } else {
            $response->setJsonContent(
                [
                    'status' => 'FOUND',
                    'data'   => [
                        'id'   => $robot->id,
                        'name' => $robot->name
                    ]
                ]
            );
        }

        return $response;
    }
);

Inserting Data

Taking the data as a JSON string inserted in the body of the request, we also use PHQL for insertion [File: index.php]:

<?php

use Phalcon\Http\Response;

// Adds a new robot
$app->post(
    '/api/robots',
    function () use ($app) {
        $robot = $app->request->getJsonRawBody();

        $phql = 'INSERT INTO Store\Toys\Robots (name, type, year) VALUES (:name:, :type:, :year:)';

        $status = $app->modelsManager->executeQuery(
            $phql,
            [
                'name' => $robot->name,
                'type' => $robot->type,
                'year' => $robot->year,
            ]
        );

        // Create a response
        $response = new Response();

        // Check if the insertion was successful
        if ($status->success() === true) {
            // Change the HTTP status
            $response->setStatusCode(201, 'Created');

            $robot->id = $status->getModel()->id;

            $response->setJsonContent(
                [
                    'status' => 'OK',
                    'data'   => $robot,
                ]
            );
        } else {
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict');

            // Send errors to the client
            $errors = [];

            foreach ($status->getMessages() as $message) {
                $errors[] = $message->getMessage();
            }

            $response->setJsonContent(
                [
                    'status'   => 'ERROR',
                    'messages' => $errors,
                ]
            );
        }

        return $response;
    }
);

Updating Data

The data update is similar to insertion. The id passed as parameter indicates what robot must be updated [File: index.php]:

<?php

use Phalcon\Http\Response;

// Updates robots based on primary key
$app->put(
    '/api/robots/{id:[0-9]+}',
    function ($id) use ($app) {
        $robot = $app->request->getJsonRawBody();

        $phql = 'UPDATE Store\Toys\Robots SET name = :name:, type = :type:, year = :year: WHERE id = :id:';

        $status = $app->modelsManager->executeQuery(
            $phql,
            [
                'id'   => $id,
                'name' => $robot->name,
                'type' => $robot->type,
                'year' => $robot->year,
            ]
        );

        // Create a response
        $response = new Response();

        // Check if the insertion was successful
        if ($status->success() === true) {
            $response->setJsonContent(
                [
                    'status' => 'OK'
                ]
            );
        } else {
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict');

            $errors = [];

            foreach ($status->getMessages() as $message) {
                $errors[] = $message->getMessage();
            }

            $response->setJsonContent(
                [
                    'status'   => 'ERROR',
                    'messages' => $errors,
                ]
            );
        }

        return $response;
    }
);

Deleting Data

The data delete is similar to update. The id passed as parameter indicates what robot must be deleted [File: index.php]:

<?php

use Phalcon\Http\Response;

// Deletes robots based on primary key
$app->delete(
    '/api/robots/{id:[0-9]+}',
    function ($id) use ($app) {
        $phql = 'DELETE FROM Store\Toys\Robots WHERE id = :id:';

        $status = $app->modelsManager->executeQuery(
            $phql,
            [
                'id' => $id,
            ]
        );

        // Create a response
        $response = new Response();

        if ($status->success() === true) {
            $response->setJsonContent(
                [
                    'status' => 'OK'
                ]
            );
        } else {
            // Change the HTTP status
            $response->setStatusCode(409, 'Conflict');

            $errors = [];

            foreach ($status->getMessages() as $message) {
                $errors[] = $message->getMessage();
            }

            $response->setJsonContent(
                [
                    'status'   => 'ERROR',
                    'messages' => $errors,
                ]
            );
        }

        return $response;
    }
);

Testing our Application

Using curl we'll test every route in our application verifying its proper operation.

Obtain all the robots:

curl -i -X GET http://localhost/my-rest-api/api/robots

HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:05:13 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 117
Content-Type: text/html; charset=UTF-8

[{"id":"1","name":"Robotina"},{"id":"2","name":"Astro Boy"},{"id":"3","name":"Terminator"}]

Search a robot by its name:

curl -i -X GET http://localhost/my-rest-api/api/robots/search/Astro

HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:09:23 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 31
Content-Type: text/html; charset=UTF-8

[{"id":"2","name":"Astro Boy"}]

Obtain a robot by its id:

curl -i -X GET http://localhost/my-rest-api/api/robots/3

HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 07:12:18 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 56
Content-Type: text/html; charset=UTF-8

{"status":"FOUND","data":{"id":"3","name":"Terminator"}}

Insert a new robot:

curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}'
    http://localhost/my-rest-api/api/robots

HTTP/1.1 201 Created
Date: Tue, 21 Jul 2015 07:15:09 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 75
Content-Type: text/html; charset=UTF-8

{"status":"OK","data":{"name":"C-3PO","type":"droid","year":1977,"id":"4"}}

Try to insert a new robot with the name of an existing robot:

curl -i -X POST -d '{"name":"C-3PO","type":"droid","year":1977}'
    http://localhost/my-rest-api/api/robots

HTTP/1.1 409 Conflict
Date: Tue, 21 Jul 2015 07:18:28 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 63
Content-Type: text/html; charset=UTF-8

{"status":"ERROR","messages":["The robot name must be unique"]}

Or update a robot with an unknown type:

curl -i -X PUT -d '{"name":"ASIMO","type":"humanoid","year":2000}'
    http://localhost/my-rest-api/api/robots/4

HTTP/1.1 409 Conflict
Date: Tue, 21 Jul 2015 08:48:01 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 104
Content-Type: text/html; charset=UTF-8

{"status":"ERROR","messages":["Value of field 'type' must be part of
    list: droid, mechanical, virtual"]}

Finally, delete a robot:

curl -i -X DELETE http://localhost/my-rest-api/api/robots/4

HTTP/1.1 200 OK
Date: Tue, 21 Jul 2015 08:49:29 GMT
Server: Apache/2.2.22 (Unix) DAV/2
Content-Length: 15
Content-Type: text/html; charset=UTF-8

{"status":"OK"}

Conclusion

As we saw, developing a RESTful API with Phalcon is easy using micro applications and PHQL.

99月/170

Centos安装PHP的IMAP模块LNMP

发布在 邵珠庆

学习记录一些 Linux 上的东西:

1.首先 ssh 连接上你的服务器:然后执行以下代码:

2.然后准备安装:

3.然后把编译好的静态模块添加进 php.ini 文件就好:

4.然后重启 PHP:

107月/170

WebSocket全双工通信入门教程

发布在 邵珠庆

 1. 什么是WebSocket
           WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。
                1. 传统的web通信方式
                        1. 工作模式:客户端请求-服务端响应
                        2. 适用场景:信息变化不是特别频繁的场合,如网页刷新
                        3. 不适用场景:在线游戏,实时监控
                        4. 问题
                          占用网络传输带宽:每次请求和应答都带有完整的Http头,这就增加了每次传输的数据量。
                          实时性差:在全双工通信时常采用轮询进行。

                2. 改进版web通信方式
                        1. 轮询
                          
                                1. 基于polling(轮询)技术:以频繁请求方式来保持客户端和服务端的同步
                                2. 问题:客户端的频繁的请求,服务端的数据无变化,造成通信低效
                        2. 长轮询
                          
                                1. 当服务端没有数据更新的时候,连接会保持一段时间周期直到数据或者状态改变或者过期,以此减少无效的客户端和服务端的交互
                                2. 当服务端数据变更频繁的话,这种机制和定时轮询毫无区别
                        3. 流技术
                          
                                1. 在客户端页面通过一个隐藏的窗口向服务端发出一个长连接请求。服务端接到这个请求后作出回应并不断更新链接状态以保证客户端和服务端的连接不过期。
                                2. 浏览器设计兼容和并发处理问题。
                        4. websocket
                                1. 概念:是html5开始提供的一种在单个TCP连接上进行全双工通讯协议。Websocket通信协议与2011年倍IETF定为标准RFC 6455,Websocket API被W3C定为标准。
                                2. 原理和TCP一样,只需做一个握手动作,就可以形成一条快速通道。
                                           
                                        1. 客户端发起http请求,附加头信息为:“Upgrade Websocket”
                                            
                                            
                                        2. 服务端解析,并返回握手信息,从而建立连接
                                            
                                        3. 传输数据(双向)
                                           
                                        4. 断开连接
                                3. 数据传输过程
                                        1. 以帧形式进行传输
                                                1. 大数据分片
                                                2. 支持边生成数据,把你传递消息
                                        2. 说明
                                                1. 客户端到服务端的数据帧必须进行掩码处理,服务端拖接收到未经掩码处理的数据帧,须主动关闭连接
                                                2. 服务端到客户端的数据一定不能加掩码,客户端接收到经过掩码的实际帧,须主动关闭连接
                                                3. 发现错误的一方可以发送close帧,关闭连接。
                                4. 优势
                                        1. 数据传输量大
                                        2. 稳定性高
                                5. 支持浏览器
                                        1. Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持

       2. websocket案例说明
                1. 客户端
                        1. 创建Websocket实例
                          ws = new WebSocket("ws://127.0.0.1:10000/Server_Test.php");
1. 服务器IP:ws://127.0.0.1
2. 服务器端口:10000
3. 服务程序:Server_Test.php
2. 建立连接后回调函数
ws.onopen = function(event){}
3. 收到服务端消息后回调函数
ws.onmessage = function(event){}
4. 关闭连接后回调函数
ws.onclose = function(event){}
5. 连接错误后回调函数
                          ws.onerror = function(event)
6. 发送数据
                          ws.send('服务器,您好');
7. 查看当前连接状态
                          alert(ws.readyState)

    2. 服务端
1. 创建服务端WebSocket对象,等待客户端接入
                          $master  = WebSocket("localhost",10000);

                          首先创建Websocket,然后服务端监听相应端口,等待客户端接入
                          function WebSocket($address,$port){
                            $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
                          socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
                           socket_bind($master, $address, $port)                    or die("socket_bind() failed");
                           socket_listen($master,20)                                or die("socket_listen() failed");
                          echo "Server Started : ".date('Y-m-d H:i:s')."\n";
                             echo "Master socket  : ".$master."\n";
                           echo "Listening on   : ".$address." port ".$port."\n\n";
                            return $master;
                           }
2. 从WebSocket中读取数据
                          $buffer = socket_read($socket,2048);
3. 当收到客户端首次连接请求后,生成握手信息并回复
                          $return_str = dohandshake($buffer);
                          socket_write($socket,$return_str,strlen($return_str));
4. 连接后,即可接收并解析出客户端的数据
                          $buffer = socket_read($socket,2048);
                          $data_str = decode($buffer);
5. 接收到用户数据后,直接回复客户端
                          socket_write($socket,$return_str,strlen($return_str));
6. 断开连接
                          socket_close($socket);
                        7. 以上6步完成全双工通信
                3. 本案例说明

                        1. 目前只可传文本,byte传输还需进一步研究

        3. 案例运行流程
            首先需要安装PHP study,然后将服务端代码放到PHP study可执行目录下,最后使用浏览器打开客户端代码去访问服务器即可进行通信。

                1. 安装PHP study,然后将PHP文件放到 D:\phpStudy\WWW 
                    
                2. 设置PHP study端口:
                    
                3. 在客户端程序中通过url去访问服务器:
                    

        4. 调试说明

                1. 客户端代码
                        1. html代码使用Hbuilder编辑完成
                        2. 打印信息使用
                          console.log("已经与服务器建立了连接rn当前连接状态:" + this.readyState);
3. 打印信息可以实时输出到控制台
2. 服务端代码
1. 服务端代码编辑同样在Hbuilder中完成
2. 服务端代码调试工具推荐使用Xdebug,我们目前使用的是打印错误日志方式进行调试
                          error_log("客户端发送数据:  $data_str \r\n", 3, "D:/phpStudy/WWW/error.log");
                        3. 注意,D:/phpStudy/WWW/error.log为日志输出路径

        5. 参考资料

                1. 认识Websocket:http://www.itpub.net/thread-1373652-1-1.html###
                2. 认识Websocket (较全面):https://my.oschina.net/u/1266171/blog/357488
                3. websocket讲解:http://wenku.baidu.com/link?url=q-RIde8I-ScuPv6wO1APa8lfX47B-6meTdm7LTNyCcSsSxOqnqzt7WtKEXzwl6YpAVG5KzMcAMRTusknr_6T9w5RC7qbZGa0elRy6b4sTZi
                4. Websocket在线测试工具:http://www.blue-zero.com/WebSocket/
                5. 客户端很简单,关键是服务端代码:http://www.yxsss.com/sg.php?fp=0&tag=websocket
                6. 使用php创建WebSocket服务:http://www.oschina.net/code/snippet_1029305_22256
                7. PHP服务器:http://www.oschina.net/code/snippet_230665_21329
                8. 在线聊天室程序:http://www.yxsss.com/ui/sk.html
                9. 在线聊天室程序详解:http://www.bitscn.com/pdb/php/201609/732585.html
                                                     http://www.111cn.net/phper/php-cy/120076.htm
10. 服务端代码详解:http://zhidao.baidu.com/link?url=cbPCqd2rdNUMqHNKhQKEqF2j5carf01upT6PHGpFFPVLZv7ef5q5avksGyNd5P06nvIltLgHCPN68YZ6Emp2ytT4NVyi1-7CUkghun4zT1i
11. java实现Websocket:http://blog.csdn.net/u011677147/article/details/47038259

   下一页