太阳侠

我是一颗恒星


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

MVC详解

发表于 2018-06-06   |   分类于 技术日记

MVC

MVC是一种设计模式(Design pattern),也就是一种解决问题的方法和思路, 是上世纪80年代提出的,到现在已经颇有历史了。 MVC的意义在于指导开发者将数据与表现解耦,提高代码,特别是模型部分代码的复用性。

MVC不仅仅存在于Web设计中,在桌面程序开发中也是一种常见的方法。MVC的出现已经有一段历史了。 记得我最早了解到MVC的时候,是在Microsoft的Visual C++ 中的MFC中。 当时年少无知,以为是MFC中特有的东西。后来随之不断学习,才发现自己的天真。 所以说,学得越多,就越觉得自己无知。越觉得自己无知,就越懂得敬畏和谦逊。 从这个角度讲,同学们,最好不要看不起谦逊的人。

有个这么一个段子,说一天A君在圈内聚会时,朋友介绍了另一个人B君互相认识。 聚会场合嘛,这很正常,也很普遍。于是AB君小聊了一下。按国人的习惯,A君就问了“先生在哪高就?”。 B君只说了句,“谈不上高就,炒炒股。” “哦,原来是炒股的。”A君心想,虽没觉得什么不对,但心理觉得B有点low,只是没说破,也没表现出来。 过后了一段时间,一次偶然机会,发现原来B君是国内某上市公司的二股东,身家过亿。 人家没说慌,确实是炒股的……

话说远了,我们还说正题。MVC是三个单词的缩写:Model, View, Controller。 MVC是一种设计模式,目前几乎所有的Web开发框架都建立在MVC模式之上。 当然,最近几年也出现了一些诸如MVP, MVVM之类的新的设计模式。 但从技术的成熟程度和使用的广泛程度来讲,MVC仍是主流。

Yii是一个Web框架,从Web开发的分工来讲,Yii的开发工作中,承担后端的内容多一些,毕竟主要就是PHP开发。 前端主要是在HTML、JavaScript、CSS上进行开发,然后通过Yii把前端的内容管起来,如通过Assets等。 这一章要讲的MVC,主要是针对后端的。 前端的MVC严格来讲不属于Yii的范畴,这里我们就不作过多介绍。 如果想了解前端的MVC,可以看看Backbone.js Angular.js等前端框架。

MVC的三要素

MVC是模型(Model)、视图(View)、控制器(Controller)3个单词的缩写。 下面我们从这3个方面来讲解MVC中的三个要素。

  • Model是指数据模型,是对客观事物的抽象。 如一篇博客文章,我们可能会以一个Post类来表示,那么,这个Post类就是数据对象。 同时,博客文章还有一些业务逻辑,如发布、回收、评论等,这一般表现为类的方法,这也是model的内容和范畴。 对于Model,主要是数据、业务逻辑和业务规则。相对而言,这是MVC中比较稳定的部分,一般成品后不会改变。 开发初期的最重要任务,主要也是实现Model的部分。这一部分写得好,后面就可以改得少,开发起来就快。

  • View是指视图,也就是呈现给用户的一个界面,是model的具体表现形式,也是收集用户输入的地方。 如你在某个博客上看到的某一篇文章,就是某个Post类的表现形式。 View的目的在于提供与用户交互的界面。换句话说,对于用户而言,只有View是可见的、可操作的。 事实上也是如此,你不会让用户看到Model,更不会让他直接操作Model。 你只会让用户看到你想让他看的内容。 这就是View要做的事,他往往是MVC中变化频繁的部分,也是客户经常要求改来改去的地方。 今天你可能会以一种形式来展示你的博文,明天可能就变成别的表现形式了。

  • Contorller指的是控制器,主要负责与model和view打交道。 换句话说,model和view之间一般不直接打交道,他们老死不相往来。view中不会对model作任何操作, model不会输出任何用于表现的东西,如HTML代码等。这俩甩手不干了,那总得有人来干吧,只能Controller上了。 Contorller用于决定使用哪些Model,对Model执行什么操作,为视图准备哪些数据,是MVC中沟通的桥梁。

对于MVC中三者的划分并没有十分明晰的定义和界线。MVC设计模式只是一种指导思想, 让你按照model, view, controller三个方面来描述你的应用,并通过三者的交互,使应用功能得以正常运转。

其中,View的部分比较明确,就是负责显示嘛。一切与显示界面无关的东西,都不应该出现在View里面。 因此,View中一般不会出现复杂的判断语句,不会出现复杂的运算过程。 对于PHP的Web应用而言,毫无疑问,HTML是View中的主要内容。这是关于View的几个原则:

  • 负责显示界面,以HTML为主;
  • 一般没有复杂的判断语句或运算过程,可以有简单的循环语句、格式化语句。 比如,一般博客首页的文章列表,就是一种循环结构;
  • 从不调用Model的写方法。也就是说,View只从Model获取数据,而不直接改写Model,所以我们说他们老死不相往来。
  • 一般没有任何准备数据的代码,如查询数据库、组合成一定格式的字符串等。 这些一般放在Controller里面,并以变量的形式传给视图。 也就是说,视图里面要用到的数据,都是拿来就能直接用的变量。

对于Model而言,最主要就是保存事物的信息,表征事物的行为和对他可以进行的操作。 比如,Post类必然有一个用于保存博客文章标题的title属性,必然有一个删除的操作,这都是Model的内容。 以下是关于Model的几个原则:

  • 数据、行为、方法是Model的主要内容;
  • 实际工作中,Model是MVC中代码量最大,逻辑最复杂的地方,因为关于应用的大量的业务逻辑也要在这里面表示。
  • Model所提供的数据都是原始数据。也就是说,不带有任何表现层的代码。 比如,一般不会在输出的数据中添加HTML标签,这是View的工作。 但是Model可以提供有结构的数据,数组结构、队列结构、乃至其他Model等。 这个结构并非是表现层的格式,而是数据在内存中的表现。
  • 与输出不同,Model的输入数据,可以是带有表现格式的数据。 如将一篇文章保存到Post里面,内容中必然包含各种HTML标签。 因此,Model一般要对输入数据作过滤、验证和规范化等预处理。 特别是对于需要保存进数据库的,一定要对所有的输入数据作预处理。 这些预处理,有的其实是业务逻辑。比如只有主编才可以删除文章,这一验证规则既也是业务逻辑,也是权限控制。 而有些预处理,则不属于业务逻辑,比如,文章标题前后的空格应去除。
  • 注意与Controller区分开。Model是处理业务方面的逻辑,Controller只是简单的协调Model和View之间的关系, 只要是与业务有关的,就该放在Model里面。好的设计,应当是胖Model,瘦Controller。

对于Controller,主要是响应用户请求,决定使用什么视图,需要准备什么数据用来显示。 以下是有关Controller的设计原则:

  • 用于处理用户请求。 因此,对于reqeust的访问代码应该放在Controller里面,比如 $_GET $_POST 等。 但仅限于获取用户请求数据,不应该对数据有任何操作或预处理,这些工作应该交由Models来完成。
  • 调用Models的读方法,获取数据,直接传递给视图,供显示。 当涉及到多个Model时,有关的逻辑应当交给Model来完成。
  • 调用Models的类方法,对Models进行写操作。
  • 调用视图渲染函数等,形成对用户Reqeust的Response。

Model设计参考

在MVC中,Model排第一,是有一定暗示的。一是Model是整个架构中,代码量最大,复用程度最高, 也是最体现程序员设计功力的地方。 二是View和Controller相对于Model而言,在实际开发中,复用程度不高,逻辑复杂程度较低。 可以说,Model设计得好,整个MVC就好,应用开发起就顺。

因此,这一节将以Model为核心,来讲MVC的设计。 实话说,MVC尽管提出了Model View Controller的划分思想,但到了具体实操中,并不是很好把握的。 下面介绍的设计参考,也仅仅是个人在实际项目中的一些体会和想法,仅作参考。 在具体设计中,可以后把握这么几点:

Model应当集中整个应用的数据和业务逻辑

应用当中涉及到的所有业务对象都应尽可能抽像成Model。 如,博客系统当中,文章要抽象成Post,评论要抽象成Comment。 而相关的业务逻辑,如发布新文章可以用
Post::create() ,删除评论可以用 Comment::delete() 。 这样子整个应用就显得很清晰明了。

基础Model应当尽可能细化

在一个应用中,特别是对于大型复杂应用,Model间关系可能比较复杂。在构造应用时,特别是基础Model时, 要从足够小的粒度来设计。 此时,就要考虑采取继承、封装等措施了。 比如,一个博客文章Post,一般包含了若干标签,在页上一般写在作者、日期等Post字段的旁边。 从逻辑上来看,把标签作为Post的一个属性,是说得通的。 但是如果把标签作为一个属性像标题、正文等字段一样依附于Post。那么在有的功能上,实现起来是有难度的。 比如,客户要求,当一个Post含有标签 “yii, model” 时,可以点击 “yii” , 然后系统列出所有具标签中含有 “yii” 的文章。

为了实现这个功能,正确的设计是单独将标签抽象成Tag。这样,Post和Tag是多对多的关系, 即一个Post有多个Tag,一个Tag也对应多个Post。这个多对多关系可以通过一张数据表 tbl_post_tag 来表示。 接下来,为Post增加 Post::getTags() 方法,并通过 tbl_post_tag 表来查询当前Post的所有标签。 同时,为Tag增加 Tag::getPosts() 方法,也通过 tbl_post_tag 表来查询当前Tag对应的文章。 这样,就具备了实现客户要求的新功能的基础。

因此,在Model设计上,要以尽量小的粒度进行设计。一般而言,粒度越小,复用的可能性就越高。

有的读者可能会问了,既然要求粒度尽可能地小,那么,Post是不是也应当再细化,把段落抽象为Model? 是否有这个必要,看客户需求。一般情况确实没有这必要,如果这么做,那是不是再以句子为单位进行抽象? 但如果客户要求这个博客系统的评论是针对段落进行的评论的, 要将评论显示在对应的段落旁边,甚至显示每个段落评论人次等功能。那么就需要把段落抽象成Model了。

分层次设计Model

从设计流程上,数据库结构设计与Model的设计是紧密相关的。先有数据库结构设计,后有Model设计。 在设计数据库结构的时候,也是在设计Model。 一般而言,最单元、粒度最小的Model就是根据每个数据库表所生成的Model,这往往是个Active Record。

比如标签的问题,在数据库存储过程中,Post和Tag是分开存的,而且这两个表的字段,没有冗余。 tbl_post_tag 表也只记录他们的ID,没有实质内容。

在获取数据渲染视图,向用户展现时,这两个Model及他们的字段,是完全够用,且没有冗余的。

那么,能不能说 Post 和 Tag 这两个Model是够用的呢?显然还不够。

当用户在创建文章、修改文章、审核文章时,需要采用一个表单来显示来收集用户输入。 其中,对于标签的采集,一般是一个长条的文本框,让用户一次性输入多个标签,并以, 等进行分隔的。

但是,这个文本框没有一个字段与之进行对应。我们也没办法对这个字段的用户输入进行任何的验证、预处理。

因此,Post的功能是不够用的。不够用怎么办?那就加吧。但直接在 Post 里面加个 public $tagString 并不好。 毕竟只是在使用表单时,才会有这个问题,其他场合,这个字段是没用的。

这种情况下,一般使用继承:

public class PostForm extends Post
{
    public $tagString;

    ... ...
}

这样,当控制器发现用户在创建、修改、审核文章时,可以使用 PostForm Model来渲染视图了, 而其他场合则仍使用Post。这样就在需要时,增加了一个 tagString 的字段用于收集用户输入的标签。

在具体设计过程中,由于Model本身就会包含很多代码,因此,要多使用这继承等手段,把代码组织好。

仔细为Model方法命名

由于Model的代码量比较大,又集中了大量的逻辑,因此,会在一个Model中有大量的方法。仍然以Post为例, 会涉及到创建、审核、发布、回收等流程,相关的方法比较多,在命名上要用心。 可能会涉及到的、名字又比较接近的方法就有:

  • getPrevPost(),前一篇文章,用于导航
  • getNextPost(),下一篇文章,用于导航
  • getRelatedPosts($n = 10),获取相关的N篇文章,用于相关文章推荐列表
  • getPostsOfAuthor($n = 10),获取同一作者的N篇相关文章,用于作者文章列表
  • getLatestPosts($n = 10),最新的N篇文章,静态方法,用于文章列表或RSS输出
  • getHotestPosts($n = 10),最热门的N篇文章,静态方法,用于热门文章列表
  • getPublishPosts($n = -1),获取已经发布的N篇文章,静态方法,用于文章列表
  • getDraftPosts($n = -1),获取未发布的N篇文章,静态方法,用于作者页面

这里只是一些获取其他Post的方法,命名比较合理,一看就知道意思。 而且全部写成getter的形式,可以使用读取属性的方式进行访问。

不单单是在Model方法的命名上要用心, 在变量名、类名、方法名等的命名上,也要养成习惯,形成规律。 不要图一时之快,胡乱起名。否则,出来混,迟早要还的。

MVC与前后端的配合

从MVC的起源来讲,是从桌面应用的开发中发展起来的。从本质来讲,这是一种解决问题的思路和办法。 从实践来讲,这是一种久经考验的有效方式。但是如开头我们讲的,Yii更多的是侧重于后端。 对于Web应用而言,包含Yii在内的许多Web开发框架,都是没有办法知道用户的操作,如鼠标、键盘等操作的。 Web应用想要了解用户的操作,只能依靠用户发送Request。 而对于鼠标、键盘等的响应,只能依靠前端技术,如JavaScript等来实现。

再加上这几年来Web浏览器的功能日臻强大。因此,Web应用开发出现了一个新的发展苗头,就是功能从后端往前端转移。

在前端,通过JavaScript捕获用户操作,进行相应处理。 或是发送回后端获取响应后处理,如通过ajax请求后端数据,实现无刷新的局部页面更新,向用户进行反馈; 或直接在前端由浏览器进行处理,如使用backbone.js、Angular.js等前端框架的数据绑定功能等。 这些都使得Web应用表现得越来越像桌面应用。

后端MVC也在为前后端的发展而改变。 Controller的功能更多的变成了识别是ajax请求还是普通请求, 并根据请求的不同采取相应的视图渲染方式。对于普通请求,正常渲染视图,输出HTML。 对于ajax请求,则返回局部渲染视图,输出HTML片段。有的甚至输出XML或者JSON。 唯一在大潮流中,巍然不动的,还是我们的大Model。

参考链接

Discuz因图片分辨率过高造成提示“没有合法的文件被上传”的解决方案

发表于 2018-05-25   |   分类于 技术日记

用相机拍的照片的分辨率是特别高的,分辨率达到3000*5000px以上级别,这么高的图片的品质肯定是杠杠的,但在网页上面来讲确实太大了,因为我们普通的网页也用不着展示这么高清这么大的图片。我觉得原因有二:一是因为我们普通的网页确实不需要这么大的;二是这么大的图片通常有好几兆甚至十几兆大小,这样页面的加载速度回很慢的。

我们在Discuz中遇到一个问题,那就是上传某些图片的时候提示“没有合法的文件被上传”,而不是说您上传的文件太大了。想到不合法通常会想到的是图片的格式不对,不是普通的jpg或者png之类的,而是其他的非主流的图片格式。但不管把这样的图片转变成png还是jpg都不能上传,但吧图片一缩小却是可以上传的。这样不就是因为文件大小的问题么?(越大一般来讲文件体积也越大)。但检查后发现文件体积并没有超过网站限制以及对应服务器的php配置。用相机拍的照片的分辨率是特别高的,分辨率达到3000*5000px以上级别,这么高的图片的品质肯定是杠杠的,但在网页上面来讲确实太大了,因为我们普通的网页也用不着展示这么高清这么大的图片。我觉得原因有二:一是因为我们普通的网页确实不需要这么大的;二是这么大的图片通常有好几兆甚至十几兆大小,这样页面的加载速度回很慢的。

我们在Discuz中遇到一个问题,那就是上传某些图片的时候提示“没有合法的文件被上传”,而不是说您上传的文件太大了。想到不合法通常会想到的是图片的格式不对,不是普通的jpg或者png之类的,而是其他的非主流的图片格式。但不管把这样的图片转变成png还是jpg都不能上传,但吧图片一缩小却是可以上传的。这样不就是因为文件大小的问题么?(越大一般来讲文件体积也越大)。但检查后发现文件体积并没有超过网站限制以及对应服务器的

php配置

function get_image_info($target, $allowswf = false) {
    $ext = discuz_upload::fileext($target);
    $isimage = discuz_upload::is_image_ext($ext);
    if(!$isimage && ($ext != 'swf' || !$allowswf)) {
        return false;
    } elseif(!is_readable($target)) {
        return false;
    } elseif($imageinfo = @getimagesize($target)) {
        list($width, $height, $type) = !empty($imageinfo) ? $imageinfo : array('', '', '');
        $size = $width * $height;
        if($size > 16777216 || $size < 16 ) {
            return false;
        } elseif($ext == 'swf' && $type != 4 && $type != 13) {
            return false;
        } elseif($isimage && !in_array($type, array(1,2,3,6,13))) {
            return false;
        } elseif(!$allowswf && ($ext == 'swf' || $type == 4 || $type == 13)) {
            return false;
        }
        return $imageinfo;
    } else {
        return false;
    }
}

这个方法就是获取图片的信息,而返回的值就是真或者假,而为假的时候就提示非法,为真的时候验证通过,上传流程正常执行。而其中就有一个$size变量,它的值是通过获取图片的长宽,然后长宽乘积得到,默认是16777216,开根之后是4096,也就是超过4096×4096px的图片就会之前的报错。而开始的时候我们上传的是5000*4000=2000000,这个值是大于设定的临界的,于是就返回flase。

原文链接

linux定时任务精确到秒

发表于 2018-05-25   |   分类于 技术日记

linux的crond定时任务只能精确到分,如何才能做到控制到秒呢?答案肯定是明显的,实现的方法也有多种,但本文只介绍一个相对来说比较精确,又方便的写法。

使用说明

增加一个普通的定时器脚本de >dateTime.shde>,放于/目录

此脚本用于打印当前系统时间

#!/bin/bash
date

增加可执行权限

chmod +x /dateTime.sh

编写定时任务

crontab -e

脚本内容为

*/1 * * * * /dateTime.sh >> /dateTime.log
*/1 * * * * sleep 5 && /dateTime.sh >> /dateTime.log
*/1 * * * * sleep 10 && /dateTime.sh >> /dateTime.log
*/1 * * * * sleep 15 && /dateTime.sh >> /dateTime.log
*/1 * * * * sleep 20 && /dateTime.sh >> /dateTime.log
...
*/1 * * * * sleep 55 && /dateTime.sh >> /dateTime.log

重新启动crond

service crond restart

程序就会每5秒执行一次脚本,并将时间写入/dateTime.log文件中

核心

主要是使用linux的sleep控制脚本执行时间

原文链接

php发送get、post请求的6种方法简明总结

发表于 2018-05-25   |   分类于 技术日记

方法1: 用file_get_contents 以get方式获取内容:

<?php
$url='http://www.yongdev.com/';  
$html = file_get_contents($url);
echo $html;
?>

方法2: 用fopen打开url, 以get方式获取内容:

<?php
$fp = fopen($url, 'r');
stream_get_meta_data($fp);
while(!feof($fp)) {
$result .= fgets($fp, 1024);
}
echo "url body: $result";
fclose($fp);
?>

方法3:用file_get_contents函数,以post方式获取url

<?php
$data = array ('foo' => 'bar');
$data = http_build_query($data);

$opts = array (
‘http' => array (
'method' => 'POST',
'header'=> "Content-type: application/x-www-form-urlencodedrn",
"Content-Length: " . strlen($data) . "rn",
'content' => $data
)
);

$context = stream_context_create($opts);
$html = file_get_contents('http://www.yongdev.com/test.html', false, $context);

echo $html;
?>

方法4:用fsockopen函数打开url,以get方式获取完整的数据,包括header和body,fsockopen需要 PHP.ini 中 allow_url_fopen 选项开启

<?php
function get_url ($url,$cookie=false)
{
$url = parse_url($url);
$query = $url[path]."?".$url[query];
echo "Query:".$query;
$fp = fsockopen( $url[host], $url[port]?$url[port]:80 , $errno, $errstr, 30);
if (!$fp) {
return false;
} else {
$request = "GET $query HTTP/1.1rn";
$request .= "Host: $url[host]rn";
$request .= "Connection: Closern";
if($cookie) $request.="Cookie:  $cookien";
$request.="rn";
fwrite($fp,$request);
while(!@feof($fp)) {
$result .= @fgets($fp, 1024);
}
fclose($fp);
return $result;
}
}
//获取url的html部分,去掉header
function GetUrlHTML($url,$cookie=false)
{
$rowdata = get_url($url,$cookie);
if($rowdata)
{
$body= stristr($rowdata,"rnrn");
$body=substr($body,4,strlen($body));
return $body;
}

return false;
}
?>

方法5:用fsockopen函数打开url,以POST方式获取完整的数据,包括header和body

<?php
function HTTP_Post($URL,$data,$cookie, $referrer=”")
{

// parsing the given URL
$URL_Info=parse_url($URL);

// Building referrer
if($referrer=="") // if not given use this script as referrer
$referrer="111";

// making string from $data
foreach($data as $key=>$value)
$values[]="$key=".urlencode($value);
$data_string=implode("&",$values);

// Find out which port is needed – if not given use standard (=80)
if(!isset($URL_Info["port"]))
$URL_Info["port"]=80;

// building POST-request:
$request.="POST ".$URL_Info["path"]." HTTP/1.1n";
$request.="Host: ".$URL_Info["host"]."n";
$request.="Referer: $referern";
$request.="Content-type: application/x-www-form-urlencodedn";
$request.="Content-length: ".strlen($data_string)."n";
$request.="Connection: closen";

$request.="Cookie:  $cookien";

$request.="n";
$request.=$data_string."n";

$fp = fsockopen($URL_Info["host"],$URL_Info["port"]);
fputs($fp, $request);
while(!feof($fp)) {
$result .= fgets($fp, 1024);
}
fclose($fp);

return $result;
}

?>

方法6:使用curl库,使用curl库之前,可能需要查看一下php.ini是否已经打开了curl扩展

<?php
$ch = curl_init();
$timeout = 5;
curl_setopt ($ch, CURLOPT_URL, 'http://www.yongdev.com/');
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$file_contents = curl_exec($ch);
curl_close($ch);

echo $file_contents;
?>

说一下实践中遇到的问题:

使用“方法6的curl方法”过程中时执行特别慢。改为“方法3:用file_get_contents函数,以post方式”后速度快很多。

彻底理解WebService SOAP WSDL

发表于 2017-08-25   |   分类于 技术日记

WebServices简介

先给出一个概念 SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构,既然说是一种架构的话,所以一般认为 SOA 是包含了运行环境,编程模型,架构风格和相关方法论等在内的一整套新的分布式软件系统构造方法和环境,涵盖服务的整个生命周期。而在 SOA 的架构风格中,服务是最核心的抽象手段。

SOA 中的服务是构建在一些列基于开放标准的基础之上的,Web 服务定义了如何在异构系统之间实现通信的标准化方法,从而就使得 Web 服务可以跨越运行平台和实现语言,同时也使得 Web 服务成为了实现 SOA 中服务的主要技术。

至于SOA 的话,太高深的技术,这里不予讨论(嘿嘿),本篇博文只介绍 WebServices 这项技术。

引子

有没有一种办法可以实现跨应用程序进行通信和跨平台进行通信呢?

换句话说,就是有什么办法可以实现我的应用程序 A 可以和应用程序 B 进行通信呢?
或者说是,我用 Java 写的应用程序和用 . Net 开发的应用程序之间进行通信呢?
很多时候,上面提到的这些,我们是必须要使用的,比如,一个跨应用程序吧,
拿腾讯 QQ 来说吧,我估计每一个人都用过腾讯 QQ 上面的天气预报工具吧 ! ! !

上面的这个天气预报功能是如何实现的呢?

有一种办法,那就是腾讯公司放个卫星上天,并且在公司中成立一个气象部门,天天关注于天气,然后每时每刻更新腾讯 QQ 上的这个天气预报信息,确实,这种办法确实行得通,不过,要真这样做的话,估计马化腾也该被踢出去了(哪有这么蠢啊?),那么上面这个是如何实现的呢?

别急,且听我慢慢道来~~~

然后,我们再来看看跨平台这个东东又是什么呢?这里主要是拿 . Net 平台和Java 平台来说明例子,假若,有两个公司,每个公司呢都有自己的一个项目,一个公司呢使用 . Net 开发,一个呢,使用 Java 开发,恩,本来呢,这两个是相互独立的,进水不犯河水,但是有一天,突然,这两个公司给合并了,合并后的老总发现,如果把两个项目结合起来将会大大的赚一笔,为此,如何做?因为要把两个项目结合在一起,那么这两个项目之间总应该通通信吧 !!!可这两个项目又是基于不同的平台,怎么通信呢?麻烦了吧 !!!而后再看一种情况,就是比如一个公司使用的服务器是 Windows Server 2008,那么它如何和 IT 供应商的UNIX 或者是 Linux 服务器进行连接呢?也很复杂吧 !!!

WebServices特点介绍

WebServices 提供一个建立分布式应用的平台,使得运行在不同操作系统和不同设备上的软件,或者是用不同的程序语言和不同厂商的软件开发工具开发的软件,所有可能的已开发和部署的软件,能够利用这一平台实现分布式计算的目的。WebServices的思想是:使得应用程序也具有 Web 分布式编程模型的松散耦合性。

WebServices的特点:

(1),WebServices 是自包含的。即在客户端不需要附加任何软件,只要客户机支持 HTTP 和XML 就 OK 了。

(2),WebServices 是自我描述的。在客户端和服务端都不需要知道除了请求和响应消息的格式和内容外的任何事。

(3),WebServices 是跨平台和跨语言的。客户端和服务端均可以在不同的平台和语言环境中实现,同时,不必为了支持 WebServices 而更改现有的代码。

(4),WebServices 是基于开放和标准的。XML 和 HTTP 是WebServices 的主要技术基础,而 XML 和HTTP 早就成了业内标准了。

(5),WebServices 是动态的。

(6),WebServices 是可以组合的。也就是通过一个 WebService 访问另外一个 WebService 来达到组合的目的。通过组合 WebServices 便可以将简单的 WebServices 聚合成为实现更多复杂功能的复杂的服务。

(7),WebServices 是松散耦合的。它完全解耦了客户端和服务端。

(8),WebServices 提供编程访问的能力。换句话说,就是可以通过编写程序来访问Web 服务。

(9),WebServices 是基于经过考验的成熟技术上构建的。比如 XML 和 HTTP。

(10),WebServices 提供打包现有应用程序的能力。

(11),WebServices 通过网络进行发布,查找和使用。

上面这些特点呢,现在不清楚的话,也不用紧,等下还会有详细的说明的 !!!

WebServices到底是什么?

如果简单的说的话,WebServices就是一组函数库,不过这和我们平时概念中的函数库却又有不同,我们平时所使用的函数库要么是自己写的(在自己的应用程序当中写一组函数库),要么是调用底层的 API(操作系统 API 如Win32 API),上面的这两种情况有一个共同点,那就是函数库是位于客户端本地的,比如,您调用 Win32 API的话,就是调用本地操作系统上的函库,而这里提到 Web 服务也是一组函数库这个概念和上面提到的函数库这个概念的区别就在此处,因为 Web服务看做一组函数库的话,那么这组函数库不是位于本地的,而是位于远程机器上(当然也可以是本地机器中)。

何为 Web 服务?

也就是网络服务,那就是把网络上不知道那个地方的一些函数看做是一组服务,然后我再通过网络就可以使用这些服务。

关于什么是 Web 服务,上面的说法那是山寨版的,稍微正经一点的说法是:Web 服务是一种部署在 Web 上的对象或者是应用程序组件。

Why WebServices?

为什么需要使用 WebServices呢?这必须根据 WebServices 的特点以及其优势进行分析了。

首先,上面呢,也说了,Web服务的话,就是一组网络上的应用程序组件,这样的话,您便可以通过在您的应用程序中使用 Web 服务来将您的应用程序提升到服务层面上来。
既然可以看做是一组服务了的话,那么当然就是可以提供给别个(别的应用程序)使用咯。
比如,我可以通过 Web 服务来公开一些接口给别个使用,至于这些要不要收费呢?那就看我心情了,前面举了腾讯 QQ 上查询天气的例子,这个例子呢,就可以在这里来做一个解释了,
在中国,应该只有一个卫星来进行天气预报的吧?腾讯也不可能为了天气预报而专门放个卫星上天吧?

可是腾讯 QQ 又确实是可以查询天气的,这里,便可以通过 Web 服务来解决。
首先,中国气象局应该是有一个卫星的,气象局根据卫星所返回的结果实时发布全国各地的天气状况,并且将这些天气信息以 Web 服务的形式公开,然后呢,腾讯 QQ 便可以通过这个 Web 服务来访问到天气状况了,再将这些天气状况反馈到 QQ 上就 OK 了。

然后,上面提到了 Web服务是应用程序组件,既然是组件,那么就可以对这个组件重复的进行使用了,同时可以通过 Web 服务来实现将这个应用程序组件作为一个服务来进行使用,
更为强大的是,可以将多个 WebServices组合成为更为强大的 WebServices ,
并且是通过互联网哦!!!
这也是一大优点啊,

然后呢,最基本的 WebServices是基于 XML 和 HTTP 的
(当然这是最基本的 WebServices ,比如 WebServices 还可以通过 HTTPS 或者是 SMTP 来实现通信),
这又有什么好处呢?很明显,XML 和HTTP 这些都已经是标准了,
不论你是 Java 平台呢,还是 . Net 平台开发出来的(或者是是使用 Web 服务),既然我是使用 XML 和 HTTP 的话,我才懒得鸟你什么 Java 还是 . Net 呢,我也不管你是 linux 还是 Windows ,这一切都和我 Web 服务无关,我关注的只是通过 HTTP 协议来传输 XML 就 OK 了,
至于这些 XML 是如何被服务提供者开发出来的或者这些 XML 是如何被服务请求者使用的,这些都和我无关,这里便可以看出 Web 服务的另一个优势了,那就是跨语言跨平台(实现协同工作),所以可以通过 Web 服务来实现不同应用程序和不同平台之间的通信。

Web 服务允许独立于实现服务基于的硬件或者是软件平台和编写服务所用的编程语言使用服务,
根据上面这两点呢,便可以解决掉最开始提出的使用 Java 开发的应用程序如何和使用 . Net 开发的应用程序之间进行通信这一问题,同时,也可以解决 Linux 或者是UNIX 和 Windows Server 2008 之间进行连接这一问题了。最后就是通过使用不同的 Web 服务,也不管 Web 服务是那种编程语言实现的,
我们都可以从不同的平台和操作系统进行访问,从而大大提高了不同应用程序共享数据和应用的能力并且 Web服务提供了构建 SOA 所必须得技术基础。

从上面可以看出通过 WebServices解决了我们曾经想都不敢想的问题,这么强大的东西为什么不加以好好利用呢?

WebServices体系结构

在Web 服务的体系结构中,涉及到三个角色,一个是 Web 服务提供者,一个是 Web 服务中介者,还有一个就是 Web 服务请求者,同时还涉及到三类动作,即发布,查找,绑定,

Web 服务提供者:

可以发布 Web 服务,并且对使用自身服务的请求进行响应,Web 服务的拥有者,它会等待其他的服务或者是应用程序访问自己。

Web 服务请求者:

也就是 Web 服务功能的使用者,它通过服务注册中心也就是 Web 服务中介者查找到所需要的服务,再利用 SOAP 消息向 Web 服务提供者发送请求以获得服务。

Web 服务中介者:

也称为服务代理,用来注册已经发布的 Web服务提供者,并对其进行分类,同时提供搜索服务,
简单来说的话,Web 服务中介者的作用就是把一个 Web 服务请求者和合适的 Web 服务提供者联系在一起,充当一个管理者的角色,一般是通过 UDDI来实现。

发布:

通过发布操作,可以使 Web服务提供者向 Web 服务中介者注册自己的功能以及访问的接口。

发现(查找):

使得 Web 服务请求者可以通过 Web 服务中介者来查找到特点的种类的 Web 服务。

绑定:

这里就是实现让服务请求者能够使用服务提供者提供的服务了。

WebServices三种基本元素之 SOAP

SOAP 即 Simple Object AccessProtocol 也就是简单对象访问协议。

SOAP 呢,其指导理念是“唯一一个没有发明任何新技术的技术”,是一种用于访问 Web 服务的协议。因为 SOAP 基于XML 和 HTTP ,其通过XML 来实现消息描述,然后再通过 HTTP 实现消息传输。

SOAP 是用于在应用程序之间进行通信的一种通信协议。

因为是基于 XML 和HTTP 的,所以其独立于语言,独立于平台,并且因为 XML 的扩展性很好,所以基于 XML 的 SOAP 自然扩展性也不差。通过 SOAP 可以非常方便的解决互联网中消息互联互通的需求,其和其他的 Web 服务协议构建起 SOA 应用的技术基础。
SOAP 协议的一个重要特点是它独立于底层传输机制,Web 服务应用程序可以根据需要选择自己的数据传输协议,可以在发送消息时来确定相应传输机制。

由于 HTTP 协议本身的一些特点和局限性,使得当 SOAP 使用HTTP 绑定的 Web 服务并不能满足某些企业应用的需求。比如,HTTP 不是一个可靠传输协议,所以有可能在传输过程中出现问题,然后 HTTP 协议基于Request/Response 模型,也就是说客户端需要在等待响应消息接收完成后才能继续执行,而此时如果响应时间过长呢?基于上面的这些需求,便需要选择合适的传输协议了。关于这方面的内容的话,有点深奥了,有兴趣的可以去看看 IBM 的一些关于这方面内容的介绍。还有一点需要提及一下,那就是 SOAP 是可以绕过防火墙的,将来将会作为 W3C 的标准进行发展。

WebServices三种基本元素之 WSDL

WSDL 即Web Services Description Language也就是 Web 服务描述语言。
是基于 XML的用于描述 Web 服务以及如何访问 Web 服务的语言。

服务提供者通过服务描述将所有用于访问 Web服务的规范传送给服务请求者,要实现 Web服务体系结构的松散耦合,服务描述是一个关键,不管是请求者还是服务提供者,通过服务描述便可以不必了解对方的底层平台,编程语言等,
服务描述与底层的 SOAP 基础结构相结合,足以封装服务请求者的应用程序和服务提供者的 Web服务之间的这个细节。

WSDL 描述了 Web服务的三个基本属性:

(1)服务所提供的操作

(2)如何访问服务

(3)服务位于何处(通过 URL 来确定就 OK 了)

WebServices三种基本元素之 UDDI

UDDI 即 Universal Description,Discovery and Integration,也就是通用的描述,发现以及整合。WSDL 呢,用来描述了访问特定的 Web 服务的一些相关的信息,可以在互联网上,或者是在企业的不同部门之间,如何来发现我们所需要的 Web 服务呢?
而 Web 服务提供商又如何将自己开发的 Web 服务公布到因特网上,这就需要使用到 UDDI了,UDDI的话,是一个跨产业,跨平台的开放性架构,可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。

UDDI 呢是一种目录服务,企业可以通过 UDDI 来注册和搜索 Web 服务。
简单来时候话,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于 Web 服务的信息而已。并且 UDDI 通过SOAP 进行通讯,构建于 . Net 之上。

开发 Web服务的方式

(1)开发阶段:

实现一个 Web 服务,使这个 Web 服务能响应和接收 SOAP 消息,(这个呢,其实可以通过 Visual Studio 来帮助实现),定义好逻辑模块(这个 Web 服务总要干点事情吧), 然后再撰写 WSDL 文件(这个呢,开发工具会自动生成的,不需要人工撰写了)

(2)部署阶段:

指定 Web 服务的传输协议,将 Web 服务注册到相应服务描述部署文件(这些也是可以由工具来自动完成的)

(3)发布阶段:

将 Web 服务的接口和调用的地址公开给客户端调用,常用的发布方式为基于 Web 提供的WSDL 的链接,当然 UDDI 也是一个选择。

总结一下 WebServices的优点

其实呢,前面介绍的都是关于 WebServices 的优点,在这里就只是浅要的总结一下了。

首先,WebServices 是基于 Internet 和异构平台的应用,这样便可以方便的实现通过网络来通信,同时可以实现在不同的平台之间共享数据。

然后就是,WebServices 是基于 XML 和HTTP 的,也就是基于标准和开放的,基于 XML 的话,扩展性自然好,自然跨语言。
基于 HTTP 的话,自然跨平台了。

最后,再回忆一下 WebServices 是一种应用程序组件吧,这样便可以将 WebServices 重复使用了。

谈谈 WebServices 的缺点

首先就是由于 XML 文件的难以解析,所以在使用 Web 服务的时候,会消耗较多的 CPU 和内存资源,而后,SOAP 是基于XML 的,所以在网络传输中传输的是 XML 文件,
但是由 XML 文件相比于二进制文件来说,要大很多,自然就会消耗更多的网络资源了。
而后,由于通过 WSDL 解耦了Web 服务提供者和请求者,且 SOAP 消息时从发送者向接收者单向传送的,这在一定程度上造成了 WebServices 是一种无状态服务,尽管现在在 . Net 中可以通过 Session 来实现在客户端和服务端共享一些数据,
但是单单依靠 Session 来实现客户端和服务端的状态交互也太牵强了吧

WebServices 在数据绑定上也存在一些缺陷,因为所有的数据在传输中都是使用的 XML 来实现的,因此,需要在二进制数据和 XML 之间进行一个转换(通过序列化和反序列化来实现),
而在转换过程中有可能出现语义丢失的情况。

最后就是 WebServices 的技术要求相对比较高,因为涉及到底层的 HTTP 协议以及SOAP ,WSDL 和UDDL 这三大平台元素,所以学习的曲线也是比较长的,
当然,在 . Net 中可以通过 Visual Studio 非常快速和简单的开发和访问一个 Web 服务,
但这只限于在简单的使用上,而对于本质的东西,是比较难的。

后续

正如题目所言,是 WebServices 简介,既然是简介的话,那么自然就是以简为目标了,
说明一下的是,上面的这篇博文呢,源自前几天做的一个关于 WebServices 的演讲,
演讲的 PPT 还存有,有兴趣要的可以留个邮箱的。

WebServices 的实战(待续……)

之前基于的语言不同,需要重新调整:需要从.Net环境变为PHP语言,暂时不写,待续。

原文链接

如果你想做个程序员相关的论坛,请三思

发表于 2017-07-21   |   分类于 技术日记

标题中的“做”有两方面所指:

  • 开发:使用某种编程语言实现
  • 运营:使用某套程序直接搭建

无论是哪方面,如果你有做程序员相关论坛的想法,请三思而后行,因为你的竞争对手不是一般的多…. 所以在你行动之前,先了解一下吧!

下面整理了一些使用 开源 程序搭建的论坛,他们的相似点:

  • “小众”社区,用户大多是程序员
  • 基于“节点”进行分版
  • 界面都比较精致

每一项的顺序分别是:站点、项目地址、开发语言。

  1. PHPHub - PHP & Laravel 的中文社区 https://github.com/summerblue/phphub5 PHP

  2. FlarumOne 官网 中文 Flarum 第一站 https://github.com/flarumone/flarumone PHP

  3. Carbon Forum https://github.com/lincanbin/Carbon-Forum PHP

  4. youBBS - u 社区 https://github.com/ego008/youbbs PHP

  5. Xiuno BBS http://git.oschina.net/strwei/xiuno PHP

  6. Vanilla 中文社区 https://github.com/chuck911/vanilla4china PHP

  7. Laravel.so - 最新的 Laravel 技巧 https://github.com/laravelso/site PHP

  8. Get√Yii - 致力打造 Yii 中国第一社区 https://github.com/iiyii/getyii PHP

  9. 黑客派 https://github.com/b3log/symphony Java

  10. 朋也社区 https://github.com/tomoya92/pybbs Java

  11. Java 中国 https://github.com/junicorn/java-china Java

  12. Nutz 社区 https://github.com/wendal/nutz-book-project Java

  13. 前端乱炖 https://github.com/xinyu198736/htmljs Node.js

  14. Node.js 专业中文社区 https://github.com/cnodejs/nodeclub Node.js

  15. NodeBB 官方中文社区 https://github.com/NodeBB/NodeBB Node.js

  16. Discourse 中文论坛 https://github.com/discourse/discourse Ruby

  17. Ruby China https://github.com/ruby-china/ruby-china Ruby

  18. Go 语言中文网 https://github.com/studygolang/studygolang Go

  19. Golang 中国 https://github.com/jimmykuu/gopher Go

  20. F2E - 前端技术社区 https://github.com/PaulGuo/F2E.im Python

  21. Young 社区 https://github.com/shiyanhui/Young Python

欢迎大家进行补充

  • 也许每个程序员都有一颗想写论坛的心
  • PHP 不愧是世界上最好的编程语言….

推荐三个很赞的英语学习网站

发表于 2017-07-20   |   分类于 工作学习

很多读者都知道我有在学英语,对英语蠢蠢欲动的同学,纷纷给我留言,问我是怎么学习的?有什么学习渠道、学习技巧之类的么?哪有啊,我也就是随意瞎学,没有任何技巧而言,我总觉得学习哪有那么多所谓的学习技巧、学习捷径,就按部就班默默的坚持就好了。

但是虽说没什么学习捷径,但是我却发现一些不错的英语学习网站,我自己也亲自试用过,非常好用,所以推荐给大家。

1. Lingvist

这个网站其实不只是可以用来学英语,还可以学习其他语言,如西班牙语、法语、俄罗斯语等等,号称学习一门语言只需要 200 小时,不过它牛逼的是号称通过人工智能运算、大数据,推荐最适合你水平的教材,确定你的学习进度,了解你的学习难点,你的每一步动作与习惯,都会影响它的教程,亲身尝试确实很好用,适合各种水平的英语学习者,推荐大家使用。

地址:http://lingvist.com

2. 多邻国

这个网站只能学习英语和西班牙语,而且一进入网站就看到很醒目的一行字「学习语言,免费永远」,对很多人应该非常有吸引力,这个网站的特色除了免费外,通过游戏化的方式让你学习,让你的学习不那么枯燥,很有趣,让你更容易的坚持下去,也推荐大家尝试下。

地址:http://www.duolingo.cn

3. TED

想要学习的话,上面两个网站就差不多够了,适用不同的人与很多场景了,推荐多了,反而很纠结,但是除了以上两个网站之外,再推荐一个 TED,TED 大家应该都听过吧?是美国的一个非盈利组织,涉及技术、娱乐、设计,TED 演讲都很有质量,是学习英语的绝佳之地,你现在英语水平听不懂也没关系,我一直觉得英语本身其实不难,只不过我们没有这种氛围,没那种感觉,所以我没事就会听下 TED 的演讲,即使听不懂,但是时间长了,我相信自己会有点感觉得的。

地址:https://www.ted.com

以上就是我推荐给大家的三个不错的英语学习网站,拿去不谢,对了,补充下,他们都有相对应的 App,手机上学习一样方便。

相信我,学点英语没坏处,任何时候学,都不晚!

服务器端周期提醒如何设计

发表于 2017-03-16   |   分类于 服务器

服务器端周期提醒如何设计

问:

为了活跃App,运营那边提出需要实现一个提醒功能,大概需求就是到了用户设置的提醒时间,打开APP,方便用户记录。用户可以设置重复的时间,就像闹钟一样,可以自己定义哪几天需要提醒。现在我的困惑是,这个数据库如何设计呢?怎么设计会比较方便且容易扩展,我是打算后台用定时任务去轮询,发现有需要提醒的,然后服务器就发推送通知给APP。

答:

因为时间是线性的,设置的再多,其实每次需要执行的下一个时间最多也只有一个。
因此可以这进行数据库设计–下一次提醒时间,重复的时间,特定时间。到时间发送通知,然后更新数据库的下一次提醒时间。

PS:是一种不错的思路,可行性高

原文链接:https://segmentfault.com/q/1010000006929169

PHP与JSON的三点心得

发表于 2017-01-03   |   分类于 技术日记

1、json_encode处理中文

用PHP的json_encode来处理中文的时候, 中文都会被编码, 变成不可读的, 类似”\u*”的格式, 还会在一定程度上增加传输的数据量。
而在PHP5.4, 这个问题终于得以解决, Json新增了一个选项: JSON_UNESCAPED_UNICODE, 故名思议, 就是说, Json不要编码Unicode,而是现实中文本身。

参考:http://php.net/manual/en/function.json-encode.php

2、PHP进行POST提交JSON格式数据做参数

无论是使用curl还是fsockopen,都需要注意:

‘Content-Type: application/json’

同时注意不再使用http_build_query函数。

看一下
http_build_query 的定义和描述。

定义:生成 url-encoded 之后的请求字符串。
描述:使用给出的关联(或下标)数组生成一个url-encoded请求字符串。参数 formdata 可以是数组或包含属性的对象。一个formdata数组可以是简单的一维结构,也可以是由数组组成的数组(其依次可以包含其它数组)。如果在基础数组中使用了数字下标同时给出了numeric_prefix参数,此参数值将会作为基础数组中的数字下标元素的前缀。这是为了让PHP或其它CGI程序在稍后对数据进行解码时获取合法的变量名。

3、PHP处理返回JSON格式的数据

使用 json_decode($return,true);注意后面那个参数“true”,可以输出关联数组。

直接赋值给变量,即是一个数组。可直接使用。

如何解决秒杀的性能问题和超卖的讨论

发表于 2016-10-20   |   分类于 技术日记

最近业务试水电商,接了一个秒杀的活。之前经常看到淘宝的同行们讨论秒杀,讨论电商,这次终于轮到我们自己理论结合实际一次了。

ps:进入正文前先说一点个人感受,之前看淘宝的ppt感觉都懂了,等到自己出解决方案的时候发现还是有很多想不到的地方其实都没懂,再次验证了“细节是魔鬼”的理论。并且一个人的能力有限,只有大家一起讨论才能想的更周全,更细致。好了,闲话少说,下面进入正文。

一、秒杀带来了什么?

秒杀或抢购活动一般会经过【预约】【抢订单】【支付】这3个大环节,而其中【抢订单】这个环节是最考验业务提供方的抗压能力的。

抢订单环节一般会带来2个问题:

1、高并发

比较火热的秒杀在线人数都是10w起的,如此之高的在线人数对于网站架构从前到后都是一种考验。

2、超卖

任何商品都会有数量上限,如何避免成功下订单买到商品的人数不超过商品数量的上限,这是每个抢购活动都要面临的难题。

二、如何解决?

首先,产品解决方案我们就不予讨论了。我们只讨论技术解决方案

1、前端

面对高并发的抢购活动,前端常用的三板斧是【扩容】【静态化】【限流】

  A:扩容

  加机器,这是最简单的方法,通过增加前端池的整体承载量来抗峰值。

  B:静态化

  将活动页面上的所有可以静态的元素全部静态化,并尽量减少动态元素。通过CDN来抗峰值。

  C:限流

  一般都会采用IP级别的限流,即针对某一个IP,限制单位时间内发起请求数量。

  或者活动入口的时候增加游戏或者问题环节进行消峰操作。

  D:有损服务

  最后一招,在接近前端池承载能力的水位上限的时候,随机拒绝部分请求来保护活动整体的可用性。

2、后端

那么后端的数据库在高并发和超卖下会遇到什么问题呢?主要会有如下3个问题:(主要讨论写的问题,读的问题通过增加cache可以很容易的解决)

  I: 首先MySQL自身对于高并发的处理性能就会出现问题,一般来说,MySQL的处理性能会随着并发thread上升而上升,但是到了一定的并发度之后会出现明显的拐点,之后一路下降,最终甚至会比单thread的性能还要差。

  II: 其次,超卖的根结在于减库存操作是一个事务操作,需要先select,然后insert,最后update -1。最后这个-1操作是不能出现负数的,但是当多用户在有库存的情况下并发操作,出现负数这是无法避免的。

  III:最后,当减库存和高并发碰到一起的时候,由于操作的库存数目在同一行,就会出现争抢InnoDB行锁的问题,导致出现互相等待甚至死锁,从而大大降低MySQL的处理性能,最终导致前端页面出现超时异常。

针对上述问题,如何解决呢? 我们先看眼淘宝的高大上解决方案:

  I: 关闭死锁检测,提高并发处理性能。

  II:修改源代码,将排队提到进入引擎层前,降低引擎层面的并发度。

  III:组提交,降低server和引擎的交互次数,降低IO消耗。

以上内容可以参考丁奇在DTCC2013上分享的《秒杀场景下MySQL的低效》一文。在文中所有优化都使用后,TPS在高并发下,从原始的150飙升到8.5w,提升近566倍,非常吓人!!!

不过结合我们的实际,改源码这种高大上的解决方案显然有那么一点不切实际。于是小伙伴们需要讨论出一种适合我们实际情况的解决方案。以下就是我们讨论的解决方案:

首先设定一个前提,为了防止超卖现象,所有减库存操作都需要进行一次减后检查,保证减完不能等于负数。(由于MySQL事务的特性,这种方法只能降低超卖的数量,但是不可能完全避免超卖)

update number set x=x-1 where (x -1 ) >= 0;

解决方案1:

将存库从MySQL前移到Redis中,所有的写操作放到内存中,由于Redis中不存在锁故不会出现互相等待,并且由于Redis的写性能和读性能都远高于MySQL,这就解决了高并发下的性能问题。然后通过队列等异步手段,将变化的数据异步写入到DB中。

优点:解决性能问题

缺点:没有解决超卖问题,同时由于异步写入DB,存在某一时刻DB和Redis中数据不一致的风险。

解决方案2:

引入队列,然后将所有写DB操作在单队列中排队,完全串行处理。当达到库存阀值的时候就不在消费队列,并关闭购买功能。这就解决了超卖问题。

优点:解决超卖问题,略微提升性能。

缺点:性能受限于队列处理机处理性能和DB的写入性能中最短的那个,另外多商品同时抢购的时候需要准备多条队列。

解决方案3:

将写操作前移到MC中,同时利用MC的轻量级的锁机制CAS来实现减库存操作。

优点:读写在内存中,操作性能快,引入轻量级锁之后可以保证同一时刻只有一个写入成功,解决减库存问题。

缺点:没有实测,基于CAS的特性不知道高并发下是否会出现大量更新失败?不过加锁之后肯定对并发性能会有影响。

解决方案4:

将提交操作变成两段式,先申请后确认。然后利用Redis的原子自增操作(相比较MySQL的自增来说没有空洞),同时利用Redis的事务特性来发号,保证拿到小于等于库存阀值的号的人都可以成功提交订单。然后数据异步更新到DB中。

优点:解决超卖问题,库存读写都在内存中,故同时解决性能问题。

缺点:由于异步写入DB,可能存在数据不一致。另可能存在少买,也就是如果拿到号的人不真正下订单,可能库存减为0,但是订单数并没有达到库存阀值。

三、总结

1、前端三板斧【扩容】【限流】【静态化】

2、后端两条路【内存】+【排队】

四、非技术感想

1、团队的力量是无穷的,各种各样的解决方案(先不谈可行性)都是在小伙伴们七嘴八舌中讨论出来的。我们需要让所有人都发出自己的声音,不要着急去否定。

2、优化需要从整体层面去思考,不要只纠结于自己负责的部分,如果只盯着一个点思考,最后很可能就走进死胡同中了。

3、有很多东西以为读过了就懂了,其实不然。依然还是需要实践,否则别人的知识永远不可能变成自己的。

4、多思考为什么,会发生什么,不要想当然。只有这样才能深入进去,而不是留在表面。

ps:以上仅仅是我们讨论的一些方案设想,欢迎大家一起讨论各种可行方案。

原文链接

1…567…10
isunman

isunman

love IT, love Movie, love Love

98 日志
12 分类
45 标签
RSS
github

Links

知乎
© 2011 - 2025 isunman
由 Hexo 强力驱动
主题 - NexT.Mist
PV -- UV