- Web2.0->API(微服务架构,移动APP时代C/S技术)
- 如何对API项目中的公共技术抽离,建立有层级的PHP API项目 ?
- 如何做好异常处理及监控,保障API高效稳定的提供服务?
模板模式开发与API模式开发的区别
- API模式:去除掉模板的处理,专注于数据
- API无状态性
REST与SOAP、RPC的区别
- RPC所谓的远程过程调用(面向方法)
- SOA所谓的面向服务的架构(面向消息)
- REST 所谓的Representational state transfer(面向资源)
- REST与SOAP、RPC的区别
环境配置
$ docker run -d -p 8001:8001 -p 8000:8000 -p 80:80 pangee/lnmp:v1 /sbin/init
docker stats
进入环境
docker exec -it aef269e596cc /bin/bash
查看环境
uname -a
docker run -d -p 8888:8888 daocloud.io/library/centos /sbin/init
docker exec -it a257a5c7679c /bin/bash
docker run -it a257a5c7679c /bin/bash
安装lnmp稳定版
yum install wget
wget -c http://soft.vpser.net/lnmp/lnmp1.4.tar.gz && tar zxf lnmp1.4.tar.gz && cd lnmp1.4 && ./install.sh lnmp
cd ~
下载yaf
解压缩
phpize 编译
wget http://pecl.php.net/get/yaf-3.0.5.tgz
tar -zxvf yaf-3.0.5.tgz
cd
phpize 初始化configure文件
./configure --with-php-config=/usr/local/php/bin/php-config 准备编译时期的前期准备
make
make install
(实质上是把 find ./ -name 'yaf.so' 这个文件放在/usr/local/php/lib/php/extensions/no-debug-non-zts-20160303/)
vim /usr/local/php/etc/php.ini
如下
1889 [Yaf]
1890 extension=yaf.so
1891 yaf.environ="product"
添加一个虚拟域名
netstat -tpnlu
lnmp vhost add
lnmp vhost list
vim /usr/local/nginx/conf/vhost/hanxiao.com.conf
server
{
listen 80;
server_name hanxiao.com hanxiao.org;
index index.php ;
root /home/work/hanxiao;
include enable-php.conf;
if(!-e $request_filename){
reqrite ^/(.*) /index.php?$1 last;
}
access_log /home/work/logs/hanxiao.com.log;
}
用户登录注册接口
- 对MySQL的增删改查
- 实现注册,登录验证函数
- 简单封装返回JSON数据
文章类别接口(CURD,文章列表页)
一些接口实现方法
邮件接口
- 第三方整合接口
- 短信
- 支付
- Push细小
- IP地址转换
- 其他
接口信息收集
- 关注接口的整体时间开销
- xhprof收集API接口开销
log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent $request_time $upstream_response_time "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
<7php
xhprof-enable();
header("XhprofID:".$run_id);
try{
define('APPLICATION_PATH',dirnameC_-FILE)."/../");
$application =new Yaf-Application( APPLICATION_PATH."/conf/application.ini");
$application->bootstrap)->run);
]catch (Exception $e){
echo jsonencode( array('errno'=>-999999,'errmsg'=>'error.'.Se->getMessage()));
}
$xhprofData =xhprof-disable();
$XHPROF_ROOT="/home/work/imooc/application/library/ThirdParty";
include_once $XHPROF_ROOT."/xhprof_lib/utils/xhprof_lib.php";
include_once $XHPROF_ROOT."/xhprof_lib/utils/xhprof_runs.php";
$xhprof_runs =new XHProfRuns_Default();
$run_id =$xhprof_runs->save.run5xhprofData:"xhprof_foo");
Api自测脚本
<?php
require __DIR__ . '/../vendor/autoload.php';
use \Curl\Curl;
$cookieFile = "/tmp/_tmp_test_api_cookie_file_".rand();
$host = "http://127.0.0.1/?c=wxpay";
$curl = new Curl();
$curl->setCookieJar( $cookieFile );
$itemId = 1;
$uname = 'pangee';
$pwd = '12312312';
$curl->post( $host."&a=createbill&itemid=".$itemId, array());
if ($curl->error) {
die( 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n" );
} else {
$rep = json_decode( $curl->response, true );
if( $rep['errno']!==0 ) {
echo '未登录创建账单,失败为正常。返回信息:'.$rep['errmsg']."\n";
echo '尝试登陆账号...'."\n";
$curl->post( str_replace("wxpay","user",$host)."&a=login&submit=1", array(
'uname' => $uname,
'pwd' => $pwd,
));
if ($curl->error) {
die( 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n" );
} else {
$rep = json_decode( $curl->response, true );
if( $rep['errno']!==0 ) {
die( '用户登录失败,错误信息:'.$rep['errmsg']."\n" );
}
echo "登陆成功!\n";
}
$curl->post( $host."&a=createbill&itemid=".$itemId, array());
if ($curl->error) {
die( 'Error: ' . $curl->errorCode . ': ' . $curl->errorMessage . "\n" );
} else {
$rep = json_decode( $curl->response, true );
if( $rep['errno']!==0 ) {
echo '已登陆状态下,创建账单,失败。返回信息:'.$rep['errmsg']."\n";
} else {
echo "生成订单成功(登陆情况下)\n";
}
}
} else {
echo "生成订单成功(未登陆情况下)\n";
}
}
echo "微信支付接口测试完毕。\n";
$curl->close();
unlink( $cookieFile );
公共抽离
- Lib库的抽离
- SDK的统一管理
- Composer管理第三方类库
class Common_Request{
public static function request($key, $default=null,$type=null){
if( $type == 'get'){
$result = isset($_GET[$key])?trim($_GET[$key]):null;
} elseif ($type == 'post'){
$result = isset($_POST[$key])?trim($_POST[$key]):null;
} else {
$result = isset($_REQUEST[$key])?trim($_REQUEST[$key]):null;
}
if($default != null && $result==null){
$result = $default;
}
return $result;
}
public static function getRequest($key, $default=null){
return self::request($key,$default,'get');
}
public static function postRequest($key, $default=null){
return self::request($key, $default, 'post');
}
public static function response($errno=0,$data=null){
$resp = Err_Map::get($errno);
if($data != null){
$resp['data'] = $data;
}
return json_encode($resp);
}
}
DAO层
class Db_Base{
public static $errno = 0;
public static $errmsg = null;
public static $db = null;
public static function getDb(){
if(self::$db == null){
self::$db = new PDO("mysql:host=localhost;dbname=imooc_yaf;","root","root");
//防止pdo在拼接sql的时候将int转string
self::$db->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
}
return self::$db;
}
public function errno(){
return self::$errno;
}
public function errmsg(){
return self::$errmsg;
}
}
class Db_User extends Db_Base {
public function find($uname){
$query = self::getDb()->prepare("select `pwd`, `id` from `user` where `name` = ? ");
$query->execute(array($uname));
$ret = $query->fetchAll();
if( !$ret || count($ret)!=1 ) {
list(self::$errno,self::$errmsg) = Err_Map::get(1003);
return false;
}
return $ret[0];
}
public function checkExists($uname){
$query = self::getDb()->prepare("select count(*) as c from `user` where `name` = ?");
$query->execute(array($uname));
$count = $query->fetchAll();
if( $count[0]['c'] !=0 ) {
list(self::$errno, self::$errmsg) = Err_Map::get(1005);
return false;
}
return true;
}
public function addUser($uname,$password,$datetime){
$query = self::getDb()->prepare("insert into `user` (`id`,`name`,`pwd`,`reg_time`) VALUES (null,?,?,?)");
$ret = $query->execute(array($uname, $password,$datetime));
if( !$ret ) {
list(self::$errno,self::$errmsg) = Err_Map::get(1007);
return false;
}
return true;
}
}
接口异常的规范处理
class Err_Map{
const ERRMAP = array(
0 => '',
/**
* 用户类错误提示码
*/
1001 => '请通过正常渠道提交',
1002 => '用户名或密码不能为空',
1003 => '用户查找失败',
1004 => '密码错误',
1005 => '用户名已存在',
1006 => '密码太短,请输入最低8位的密码',
1007 => '注册失败,写入数据库失败',
/**
* 文章类错误提示码
*
*/
2000 => '需要管理员权限',
2001 => '请通过正常渠道提交',
2002 => '没填写完整',
2003 => '缺少必要的参数',
2004 => '找不到文章,请确认是否有该文章',
2005 => '找不到分类信息-',
2006 => '操作文章数据表失败,errinfo:',
2007 => '缺少必要的ID参数',
2008 => '更新文章状态失败',
2009 => '查询失败',
2010 => '获取分类信息失败',
2011 => '获取文章列表失败, errinfo',
2012 => '删除数据失败',
/**
* 邮箱发送错误提示码
*/
3001 => '请通过正常渠道提交',
3002 => '用户id,邮件title,邮件内容不能为空',
3003 => '邮箱信息查找失败',
3004 => '邮箱不合法',
/**
* 短信发送错误提示码
*/
4001 => '请通过正常渠道提交',
4002 => '用户Id,短信内容不能为空',
4003 => '用户手机号信息查找失败',
4004 => '手机号不符合规定',
4005 => '发送失败',
4006 => '消息发送成功,数据插入失败',
/**
* IP地址转详细地址错误提示码
*/
5001 => 'IP地址不正确',
/**
* 微信支付错误提示码
*/
6001 => '请传递正确的商品ID',
6002 => '请先登录后操作',
6003 => '',
6004 => '找不到这件商品',
6005 => '商品过期',
6006 => '商品没有库存',
6007 => '创建订单失败',
6008 => '更新库存失败',
6009 => '请传递正确的订单ID',
6010 => '',
6011 => '找不到订单信息',
6012 => '找不到商品信息',
);
public static function get($code){
if(isset(self::ERRMAP[$code])){
return array('errno'=>(0-$code), 'errmsg'=>self::ERRMAP[$code]);
}
return array('errno'=>(0-$code), 'errmsg'=>"没有定义该类错误码");
}
}
API文档自动生成
优化
- API时间开销定位与分析:
- API上下游性能优化:
- MySQL时间开销优化
- 后端服务调优
- API返回调优
- 稳定性
- 服务监控
- API负载均衡
- 服务报警
- API测试用例
打开App,阅读手记