什么是微服务?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2552 次浏览 • 2016-12-30 16:23 • 来自相关话题

#原创#如何理解组件、插件和控件的区别?

zkbhj 发表了文章 • 0 个评论 • 1845 次浏览 • 2016-12-29 14:03 • 来自相关话题

在第一篇文章中分享了什么是消息队列,在和丽姐讨论的时候,被丽姐的几个问题再一次难住了。虽然能够了解大致的含义,但是对于每一个概念的理解并不是很深入,所以,针对丽姐给我提出来的几个“概念模糊”的知识点,又做了一次深入的了解和学习,这里和大家要分享的就是“组件”、“插件”和“控件”之间的区别!

其实,我们在学习或者开发过程中会经常提到这些个词,但我相信会有很多人和我一样,并不一定真正理解和认识他们之间的区别。要知道他们之间的区别,首先我们需要知道他们的定义分别是什么,因为定义是对事物最精炼的概括和描述,也是我们认识他们的最基本内容。

他们的定义:

组件:是软件系统中具有相对独立功能、接口由契约指定、和语境有明显依赖关系、可独立部署、可组装的软件实体。是软件的一部分,软件的组成部分.

插件:插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。插件不能脱离指定的平台单独运行。因为插件需要调用原纯净系统提供的函数库或者数据。例如网页中用到的flash插件,没有它浏览器不能播放flash。

控件:控件是对数据和方法的封装。控件可以有自己的属性和方法。属性是控件数据的简单访问者。方法则是控件的一些简单而可见的功能。简单理解,就是控件是编程中用到的,按钮就算是一个控件,窗口也是等等诸如此类。


 

现在,大家对这三者都有了一个定义级别的认识,那么接下来,我们就看看他们之间有啥区别?

首先,先来看看组件:

范围最广的应该是组件,英文component,提起组件我们不应该把他和具体的技术,什么dll文件,ocx控件,activex等等联系起来,因为组件仅仅是一个概念,如果非要解释的话,那就是凡是在软件开发中用到了软件的复用,被复用的部分都可以称为组件。构件的英文也是component,所以说构件和组件其实是一个意思只是翻译的不同而已。

举个例子的话,我在之前给大家写过一个封装了文件存储平台的文件上传组件,当各个项目需要上传或者读取在存储平台中的文件时,就可以在程序中引入这个组件,并通过组件提供的属性和方法,进行相应的设置或者是上传读取。这就是组件,他可以让我们的程序灵活性更高,只需要简单的复用和拼装,完成一个“复杂”的系统的开发建设,能够提高我们代码的复用性,提高我们的开发效率。

知道了组件,来认识下最有意思的插件:

顾名思义,“插件”是允许我们动态插入的,而不是在编程的时候静态的写入的,这就是与普通的组件的区别,因为普通的组件是在编程的时候引入的。我们可以以硬件为例子,例如USB接口,主机设定了标准的接口,而不必考虑外部接口具体是什么设备只要这种设备实现主机提供的接口,两者就可以通讯。这种插件有个最大的优点就是即插即用,即支持动态的插入。

 个人开发经验中,我觉得使用的最多的是在前端开发中,例如我们在开发前端页面时,可能需要一个比较漂亮的图片展示效果,比如轮播图,我们可能并不需要自己重新编写一个新的,只需要在网上找些开源的JQuery插件,根据它规定的“规则”(如何提供数据,如何调用),拼接到你的程序中,问题就解决了。效果不满意,还可以随便更换。这就是“即插即用”的fell!

插件是组件(构件)的一种,我们可以这样给插件进行定义,那就是凡是在应用程序中已经预留接口的组件就是插件,例如:Java中jdbc技术,jdbc只是一个接口,任何一个插件制造商只要实现这个接口都可以被java平台所使用。我们还可以拿IE插件作为例子,IE中之所以可以嵌入很多的应用程序,那是因为IE允许他们插入,说的明白一点,那就是在IE的源程序中已经为这些应用程序预留了接口,只要把通知浏览器已经加载了什么插件,浏览器就会调用预留的接口调用这些所谓的插件。

最后,就是控件:

当然控件也是组件(构件)的一种,按照网上的说法,控件就是可视化的组件,我也同意这种说法,其实再从普通组件中分解出控件完全是没有必要的,因为对于开发人员来讲,可不可视对于非软件人员来说可能很重要,但是对于软件人员来说又有什么区别呢?

所以,其实在常规的web开发过程中,可能比较不常说控件这种说法。如果非要说有,我觉得表单中的一个按钮、一个input,可以认为是一个控件。


总结一小下:

组件是一个概念级别的词,范围最广,只要在软件开发中可以被“复用”的部分或者模块,都可以称之为“组件”;

插件是具体化了的组件,只要规定好了“接口”,就可以实现“即插即用”,方便快捷;

控件也是具体化了的组件,不常用,一般来讲是“可视化”的,例如表单中的按钮、文本框等。

 

好啦,关于这三货的学习就到这,你理解了吗?个人理解,肯能有些地方有偏差,望指正~ 查看全部
在第一篇文章中分享了什么是消息队列,在和丽姐讨论的时候,被丽姐的几个问题再一次难住了。虽然能够了解大致的含义,但是对于每一个概念的理解并不是很深入,所以,针对丽姐给我提出来的几个“概念模糊”的知识点,又做了一次深入的了解和学习,这里和大家要分享的就是“组件”、“插件”和“控件”之间的区别!

其实,我们在学习或者开发过程中会经常提到这些个词,但我相信会有很多人和我一样,并不一定真正理解和认识他们之间的区别。要知道他们之间的区别,首先我们需要知道他们的定义分别是什么,因为定义是对事物最精炼的概括和描述,也是我们认识他们的最基本内容。

他们的定义:


组件:是软件系统中具有相对独立功能、接口由契约指定、和语境有明显依赖关系、可独立部署、可组装的软件实体。是软件的一部分,软件的组成部分.

插件:插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。插件不能脱离指定的平台单独运行。因为插件需要调用原纯净系统提供的函数库或者数据。例如网页中用到的flash插件,没有它浏览器不能播放flash。

控件:控件是对数据和方法的封装。控件可以有自己的属性和方法。属性是控件数据的简单访问者。方法则是控件的一些简单而可见的功能。简单理解,就是控件是编程中用到的,按钮就算是一个控件,窗口也是等等诸如此类。



 

现在,大家对这三者都有了一个定义级别的认识,那么接下来,我们就看看他们之间有啥区别?

首先,先来看看组件:

范围最广的应该是组件,英文component,提起组件我们不应该把他和具体的技术,什么dll文件,ocx控件,activex等等联系起来,因为组件仅仅是一个概念,如果非要解释的话,那就是凡是在软件开发中用到了软件的复用,被复用的部分都可以称为组件。构件的英文也是component,所以说构件和组件其实是一个意思只是翻译的不同而已。

举个例子的话,我在之前给大家写过一个封装了文件存储平台的文件上传组件,当各个项目需要上传或者读取在存储平台中的文件时,就可以在程序中引入这个组件,并通过组件提供的属性和方法,进行相应的设置或者是上传读取。这就是组件,他可以让我们的程序灵活性更高,只需要简单的复用和拼装,完成一个“复杂”的系统的开发建设,能够提高我们代码的复用性,提高我们的开发效率。

知道了组件,来认识下最有意思的插件:

顾名思义,“插件”是允许我们动态插入的,而不是在编程的时候静态的写入的,这就是与普通的组件的区别,因为普通的组件是在编程的时候引入的。我们可以以硬件为例子,例如USB接口,主机设定了标准的接口,而不必考虑外部接口具体是什么设备只要这种设备实现主机提供的接口,两者就可以通讯。这种插件有个最大的优点就是即插即用,即支持动态的插入。

 个人开发经验中,我觉得使用的最多的是在前端开发中,例如我们在开发前端页面时,可能需要一个比较漂亮的图片展示效果,比如轮播图,我们可能并不需要自己重新编写一个新的,只需要在网上找些开源的JQuery插件,根据它规定的“规则”(如何提供数据,如何调用),拼接到你的程序中,问题就解决了。效果不满意,还可以随便更换。这就是“即插即用”的fell!

插件是组件(构件)的一种,我们可以这样给插件进行定义,那就是凡是在应用程序中已经预留接口的组件就是插件,例如:Java中jdbc技术,jdbc只是一个接口,任何一个插件制造商只要实现这个接口都可以被java平台所使用。我们还可以拿IE插件作为例子,IE中之所以可以嵌入很多的应用程序,那是因为IE允许他们插入,说的明白一点,那就是在IE的源程序中已经为这些应用程序预留了接口,只要把通知浏览器已经加载了什么插件,浏览器就会调用预留的接口调用这些所谓的插件。

最后,就是控件:

当然控件也是组件(构件)的一种,按照网上的说法,控件就是可视化的组件,我也同意这种说法,其实再从普通组件中分解出控件完全是没有必要的,因为对于开发人员来讲,可不可视对于非软件人员来说可能很重要,但是对于软件人员来说又有什么区别呢?

所以,其实在常规的web开发过程中,可能比较不常说控件这种说法。如果非要说有,我觉得表单中的一个按钮、一个input,可以认为是一个控件。


总结一小下:

组件是一个概念级别的词,范围最广,只要在软件开发中可以被“复用”的部分或者模块,都可以称之为“组件”;

插件是具体化了的组件,只要规定好了“接口”,就可以实现“即插即用”,方便快捷;

控件也是具体化了的组件,不常用,一般来讲是“可视化”的,例如表单中的按钮、文本框等。

 

好啦,关于这三货的学习就到这,你理解了吗?个人理解,肯能有些地方有偏差,望指正~

并行和并发有什么区别?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2772 次浏览 • 2016-12-28 15:30 • 来自相关话题

组件、插件、控件有什么区别?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2909 次浏览 • 2016-12-27 18:16 • 来自相关话题

什么是分布式系统?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2724 次浏览 • 2016-12-27 18:06 • 来自相关话题

进程和线程的区别和联系

zkbhj 发表了文章 • 0 个评论 • 1475 次浏览 • 2016-12-27 17:51 • 来自相关话题

1.定义

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

2.关系
 
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

3.区别
 
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。

 这就是进程和线程的重要区别。

4.优缺点
 
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

5.举例记忆
 
计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
 
操作系统的设计,因此可以归结为三点:

(1)以多进程形式,允许多个任务同时运行;

(2)以多线程形式,允许单个任务分成不同的部分运行;

(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。 查看全部
1.定义

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

2.关系
 
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.

相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。

3.区别
 
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。


1) 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

2) 线程的划分尺度小于进程,使得多线程程序的并发性高。

3) 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

4) 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

5) 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。


 这就是进程和线程的重要区别。

4.优缺点
 
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源的管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

5.举例记忆
 

计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。一个车间里,可以有很多工人。他们协同完成一个任务。线程就好比车间里的工人。一个进程可以包括多个线程。车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。

不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
 
操作系统的设计,因此可以归结为三点:


(1)以多进程形式,允许多个任务同时运行;

(2)以多线程形式,允许单个任务分成不同的部分运行;

(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。


GeoHash算法:将二维的点数据转换成一维的数据

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

1、Geohash的最简单的解释就是:将一个经纬度信息,转换成一个可以排序,可以比较的字符串编码。
 
2、geohash有以下几个特点:
首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。

Geohash比直接用经纬度的高效很多。
 
3、编码方式:
  
首先将纬度范围(-90, 90)平分成两个区间(-90,0)、(0, 90),如果目标纬度位于前一个区间,则编码为0,否则编码为1。由于39.92324属于(0, 90),所以取编码为1。
 
然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。

以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。 
经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。





 
解码算法与编码算法相反,先进行base32解码,然后分离出经纬度,最后根据二进制编码对经纬度范围进行细分即可,这里不再赘述。
 
PHP实现方式:
<?php
/**
* Geohash generation class
* http://blog.dixo.net/downloads/
*
* This file copyright (C) 2008 Paul Dixon (paul@elphin.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/



/**
* Encode and decode geohashes
*
*/
class Geohash
{
private $coding="0123456789bcdefghjkmnpqrstuvwxyz";
private $codingMap=array();

public function Geohash()
{
//build map from encoding char to 0 padded bitfield
for($i=0; $i<32; $i++)
{
$this->codingMap[substr($this->coding,$i,1)]=str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
}

}

/**
* Decode a geohash and return an array with decimal lat,long in it
*/
public function decode($hash)
{
//decode hash into binary string
$binary="";
$hl=strlen($hash);
for($i=0; $i<$hl; $i++)
{
$binary.=$this->codingMap[substr($hash,$i,1)];
}

//split the binary into lat and log binary strings
$bl=strlen($binary);
$blat="";
$blong="";
for ($i=0; $i<$bl; $i++)
{
if ($i%2)
$blat=$blat.substr($binary,$i,1);
else
$blong=$blong.substr($binary,$i,1);

}

//now concert to decimal
$lat=$this->binDecode($blat,-90,90);
$long=$this->binDecode($blong,-180,180);

//figure out how precise the bit count makes this calculation
$latErr=$this->calcError(strlen($blat),-90,90);
$longErr=$this->calcError(strlen($blong),-180,180);

//how many decimal places should we use? There's a little art to
//this to ensure I get the same roundings as geohash.org
$latPlaces=max(1, -round(log10($latErr))) - 1;
$longPlaces=max(1, -round(log10($longErr))) - 1;

//round it
$lat=round($lat, $latPlaces);
$long=round($long, $longPlaces);

return array($lat,$long);
}


/**
* Encode a hash from given lat and long
*/
public function encode($lat,$long)
{
//how many bits does latitude need?
$plat=$this->precision($lat);
$latbits=1;
$err=45;
while($err>$plat)
{
$latbits++;
$err/=2;
}

//how many bits does longitude need?
$plong=$this->precision($long);
$longbits=1;
$err=90;
while($err>$plong)
{
$longbits++;
$err/=2;
}

//bit counts need to be equal
$bits=max($latbits,$longbits);

//as the hash create bits in groups of 5, lets not
//waste any bits - lets bulk it up to a multiple of 5
//and favour the longitude for any odd bits
$longbits=$bits;
$latbits=$bits;
$addlong=1;
while (($longbits+$latbits)%5 != 0)
{
$longbits+=$addlong;
$latbits+=!$addlong;
$addlong=!$addlong;
}


//encode each as binary string
$blat=$this->binEncode($lat,-90,90, $latbits);
$blong=$this->binEncode($long,-180,180,$longbits);

//merge lat and long together
$binary="";
$uselong=1;
while (strlen($blat)+strlen($blong))
{
if ($uselong)
{
$binary=$binary.substr($blong,0,1);
$blong=substr($blong,1);
}
else
{
$binary=$binary.substr($blat,0,1);
$blat=substr($blat,1);
}
$uselong=!$uselong;
}

//convert binary string to hash
$hash="";
for ($i=0; $i<strlen($binary); $i+=5)
{
$n=bindec(substr($binary,$i,5));
$hash=$hash.$this->coding[$n];
}


return $hash;
}

/**
* What's the maximum error for $bits bits covering a range $min to $max
*/
private function calcError($bits,$min,$max)
{
$err=($max-$min)/2;
while ($bits--)
$err/=2;
return $err;
}

/*
* returns precision of number
* precision of 42 is 0.5
* precision of 42.4 is 0.05
* precision of 42.41 is 0.005 etc
*/
private function precision($number)
{
$precision=0;
$pt=strpos($number,'.');
if ($pt!==false)
{
$precision=-(strlen($number)-$pt-1);
}

return pow(10,$precision)/2;
}


/**
* create binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*/
private function binEncode($number, $min, $max, $bitcount)
{
if ($bitcount==0)
return "";

#echo "$bitcount: $min $max<br>";

//this is our mid point - we will produce a bit to say
//whether $number is above or below this mid point
$mid=($min+$max)/2;
if ($number>$mid)
return "1".$this->binEncode($number, $mid, $max,$bitcount-1);
else
return "0".$this->binEncode($number, $min, $mid,$bitcount-1);
}


/**
* decodes binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*/
private function binDecode($binary, $min, $max)
{
$mid=($min+$max)/2;

if (strlen($binary)==0)
return $mid;

$bit=substr($binary,0,1);
$binary=substr($binary,1);

if ($bit==1)
return $this->binDecode($binary, $mid, $max);
else
return $this->binDecode($binary, $min, $mid);
}
}


?>
4、感性认识:
 
1)GeoHash将二维的经纬度转换成字符串,比如下图展示了北京9个区域的GeoHash字符串,分别是WX4ER,WX4G2、WX4G3等等,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存,比如左上角这个区域内的用户不断发送位置信息请求餐馆数据,由于这些用户的GeoHash字符串都是WX4ER,所以可以把WX4ER当作key,把该区域的餐馆信息当作value来进行缓存,而如果不使用GeoHash的话,由于区域内的用户传来的经纬度是各不相同的,很难做缓存。





 
2)字符串越长,表示的范围越精确。如图所示,5位的编码能表示10平方千米范围的矩形区域,而6位编码能表示更精细的区域(约0.34平方千米)





 
3)字符串相似的表示距离相近(特殊情况后文阐述),这样可以利用字符串的前缀匹配来查询附近的POI信息。如下两个图所示,一个在城区,一个在郊区,城区的GeoHash字符串之间比较相似,郊区的字符串之间也比较相似,而城区和郊区的GeoHash字符串相似程度要低些。
 










 
通过上面的介绍我们知道了GeoHash就是一种将经纬度转换成字符串的方法,并且使得在大部分情况下,字符串前缀匹配越多的距离越近,回到我们的案例,根据所在位置查询来查询附近餐馆时,只需要将所在位置经纬度转换成GeoHash字符串,并与各个餐馆的GeoHash字符串进行前缀匹配,匹配越多的距离越近。
 
5、使用注意点:
1)由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,这样在查询附近POI信息时会导致以下问题,比如红色的点是我们的位置,绿色的两个点分别是附近的两个餐馆,但是在查询的时候会发现距离较远餐馆的GeoHash编码与我们一样(因为在同一个GeoHash区域块上),而较近餐馆的GeoHash编码与我们不一致。这个问题往往产生在边界处。





 
解决的思路很简单,我们查询时,除了使用定位点的GeoHash编码进行匹配外,还使用周围8个区域的GeoHash编码,这样可以避免这个问题。

2)我们已经知道现有的GeoHash算法使用的是Peano空间填充曲线,这种曲线会产生突变,造成了编码虽然相似但距离可能相差很大的问题,因此在查询附近餐馆时候,首先筛选GeoHash编码相似的POI点,然后进行实际距离计算。
学习参考文档:http://www.cnblogs.com/dengxinglin/archive/2012/12/14/2817761.html
http://blog.jobbole.com/80633/
  查看全部
1、Geohash的最简单的解释就是:将一个经纬度信息,转换成一个可以排序,可以比较的字符串编码。
 
2、geohash有以下几个特点:
  • 首先,geohash用一个字符串表示经度和纬度两个坐标。某些情况下无法在两列上同时应用索引 (例如MySQL 4之前的版本,Google App Engine的数据层等),利用geohash,只需在一列上应用索引即可。
  • 其次,geohash表示的并不是一个点,而是一个矩形区域。比如编码wx4g0ec19,它表示的是一个矩形区域。 使用者可以发布地址编码,既能表明自己位于北海公园附近,又不至于暴露自己的精确坐标,有助于隐私保护。
  • 第三,编码的前缀可以表示更大的区域。例如wx4g0ec1,它的前缀wx4g0e表示包含编码wx4g0ec1在内的更大范围。 这个特性可以用于附近地点搜索。首先根据用户当前坐标计算geohash(例如wx4g0ec1)然后取其前缀进行查询 (SELECT * FROM place WHERE geohash LIKE 'wx4g0e%'),即可查询附近的所有地点。


Geohash比直接用经纬度的高效很多。
 
3、编码方式:
  
首先将纬度范围(-90, 90)平分成两个区间(-90,0)、(0, 90),如果目标纬度位于前一个区间,则编码为0,否则编码为1。由于39.92324属于(0, 90),所以取编码为1。
 
然后再将(0, 90)分成 (0, 45), (45, 90)两个区间,而39.92324位于(0, 45),所以编码为0。

以此类推,直到精度符合要求为止,得到纬度编码为1011 1000 1100 0111 1001。 
经度也用同样的算法,对(-180, 180)依次细分,得到116.3906的编码为1101 0010 1100 0100 0100。

7178f37ejw1emyk4yfexpj20hj04tmy1.jpg

 
解码算法与编码算法相反,先进行base32解码,然后分离出经纬度,最后根据二进制编码对经纬度范围进行细分即可,这里不再赘述。
 
PHP实现方式:
<?php
/**
* Geohash generation class
* http://blog.dixo.net/downloads/
*
* This file copyright (C) 2008 Paul Dixon (paul@elphin.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/



/**
* Encode and decode geohashes
*
*/
class Geohash
{
private $coding="0123456789bcdefghjkmnpqrstuvwxyz";
private $codingMap=array();

public function Geohash()
{
//build map from encoding char to 0 padded bitfield
for($i=0; $i<32; $i++)
{
$this->codingMap[substr($this->coding,$i,1)]=str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
}

}

/**
* Decode a geohash and return an array with decimal lat,long in it
*/
public function decode($hash)
{
//decode hash into binary string
$binary="";
$hl=strlen($hash);
for($i=0; $i<$hl; $i++)
{
$binary.=$this->codingMap[substr($hash,$i,1)];
}

//split the binary into lat and log binary strings
$bl=strlen($binary);
$blat="";
$blong="";
for ($i=0; $i<$bl; $i++)
{
if ($i%2)
$blat=$blat.substr($binary,$i,1);
else
$blong=$blong.substr($binary,$i,1);

}

//now concert to decimal
$lat=$this->binDecode($blat,-90,90);
$long=$this->binDecode($blong,-180,180);

//figure out how precise the bit count makes this calculation
$latErr=$this->calcError(strlen($blat),-90,90);
$longErr=$this->calcError(strlen($blong),-180,180);

//how many decimal places should we use? There's a little art to
//this to ensure I get the same roundings as geohash.org
$latPlaces=max(1, -round(log10($latErr))) - 1;
$longPlaces=max(1, -round(log10($longErr))) - 1;

//round it
$lat=round($lat, $latPlaces);
$long=round($long, $longPlaces);

return array($lat,$long);
}


/**
* Encode a hash from given lat and long
*/
public function encode($lat,$long)
{
//how many bits does latitude need?
$plat=$this->precision($lat);
$latbits=1;
$err=45;
while($err>$plat)
{
$latbits++;
$err/=2;
}

//how many bits does longitude need?
$plong=$this->precision($long);
$longbits=1;
$err=90;
while($err>$plong)
{
$longbits++;
$err/=2;
}

//bit counts need to be equal
$bits=max($latbits,$longbits);

//as the hash create bits in groups of 5, lets not
//waste any bits - lets bulk it up to a multiple of 5
//and favour the longitude for any odd bits
$longbits=$bits;
$latbits=$bits;
$addlong=1;
while (($longbits+$latbits)%5 != 0)
{
$longbits+=$addlong;
$latbits+=!$addlong;
$addlong=!$addlong;
}


//encode each as binary string
$blat=$this->binEncode($lat,-90,90, $latbits);
$blong=$this->binEncode($long,-180,180,$longbits);

//merge lat and long together
$binary="";
$uselong=1;
while (strlen($blat)+strlen($blong))
{
if ($uselong)
{
$binary=$binary.substr($blong,0,1);
$blong=substr($blong,1);
}
else
{
$binary=$binary.substr($blat,0,1);
$blat=substr($blat,1);
}
$uselong=!$uselong;
}

//convert binary string to hash
$hash="";
for ($i=0; $i<strlen($binary); $i+=5)
{
$n=bindec(substr($binary,$i,5));
$hash=$hash.$this->coding[$n];
}


return $hash;
}

/**
* What's the maximum error for $bits bits covering a range $min to $max
*/
private function calcError($bits,$min,$max)
{
$err=($max-$min)/2;
while ($bits--)
$err/=2;
return $err;
}

/*
* returns precision of number
* precision of 42 is 0.5
* precision of 42.4 is 0.05
* precision of 42.41 is 0.005 etc
*/
private function precision($number)
{
$precision=0;
$pt=strpos($number,'.');
if ($pt!==false)
{
$precision=-(strlen($number)-$pt-1);
}

return pow(10,$precision)/2;
}


/**
* create binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*/
private function binEncode($number, $min, $max, $bitcount)
{
if ($bitcount==0)
return "";

#echo "$bitcount: $min $max<br>";

//this is our mid point - we will produce a bit to say
//whether $number is above or below this mid point
$mid=($min+$max)/2;
if ($number>$mid)
return "1".$this->binEncode($number, $mid, $max,$bitcount-1);
else
return "0".$this->binEncode($number, $min, $mid,$bitcount-1);
}


/**
* decodes binary encoding of number as detailed in http://en.wikipedia.org/wiki/Geohash#Example
* removing the tail recursion is left an exercise for the reader
*/
private function binDecode($binary, $min, $max)
{
$mid=($min+$max)/2;

if (strlen($binary)==0)
return $mid;

$bit=substr($binary,0,1);
$binary=substr($binary,1);

if ($bit==1)
return $this->binDecode($binary, $mid, $max);
else
return $this->binDecode($binary, $min, $mid);
}
}


?>

4、感性认识:
 
1)GeoHash将二维的经纬度转换成字符串,比如下图展示了北京9个区域的GeoHash字符串,分别是WX4ER,WX4G2、WX4G3等等,每一个字符串代表了某一矩形区域。也就是说,这个矩形区域内所有的点(经纬度坐标)都共享相同的GeoHash字符串,这样既可以保护隐私(只表示大概区域位置而不是具体的点),又比较容易做缓存,比如左上角这个区域内的用户不断发送位置信息请求餐馆数据,由于这些用户的GeoHash字符串都是WX4ER,所以可以把WX4ER当作key,把该区域的餐馆信息当作value来进行缓存,而如果不使用GeoHash的话,由于区域内的用户传来的经纬度是各不相同的,很难做缓存。

7178f37ejw1emyk4wbz4bj206i06lt9b.jpg

 
2)字符串越长,表示的范围越精确。如图所示,5位的编码能表示10平方千米范围的矩形区域,而6位编码能表示更精细的区域(约0.34平方千米)

7178f37ejw1emyk4wmjhsj208z065aad.jpg

 
3)字符串相似的表示距离相近(特殊情况后文阐述),这样可以利用字符串的前缀匹配来查询附近的POI信息。如下两个图所示,一个在城区,一个在郊区,城区的GeoHash字符串之间比较相似,郊区的字符串之间也比较相似,而城区和郊区的GeoHash字符串相似程度要低些。
 

7178f37ejw1emyk4wyfg1j206i06lt9b.jpg


7178f37ejw1emyk4xfxhfj206006s0t6.jpg

 
通过上面的介绍我们知道了GeoHash就是一种将经纬度转换成字符串的方法,并且使得在大部分情况下,字符串前缀匹配越多的距离越近,回到我们的案例,根据所在位置查询来查询附近餐馆时,只需要将所在位置经纬度转换成GeoHash字符串,并与各个餐馆的GeoHash字符串进行前缀匹配,匹配越多的距离越近。
 
5、使用注意点:
1)由于GeoHash是将区域划分为一个个规则矩形,并对每个矩形进行编码,这样在查询附近POI信息时会导致以下问题,比如红色的点是我们的位置,绿色的两个点分别是附近的两个餐馆,但是在查询的时候会发现距离较远餐馆的GeoHash编码与我们一样(因为在同一个GeoHash区域块上),而较近餐馆的GeoHash编码与我们不一致。这个问题往往产生在边界处。

7178f37ejw1emyk4zpab1j205l062gm1.jpg

 
解决的思路很简单,我们查询时,除了使用定位点的GeoHash编码进行匹配外,还使用周围8个区域的GeoHash编码,这样可以避免这个问题。

2)我们已经知道现有的GeoHash算法使用的是Peano空间填充曲线,这种曲线会产生突变,造成了编码虽然相似但距离可能相差很大的问题,因此在查询附近餐馆时候,首先筛选GeoHash编码相似的POI点,然后进行实际距离计算。
学习参考文档:http://www.cnblogs.com/dengxinglin/archive/2012/12/14/2817761.html
http://blog.jobbole.com/80633/
 

PHP开发者需要了解的Composer

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

Composer是一个非常流行的PHP包依赖管理工具,已经取代PEAR包管理器,对于PHP开发者来说掌握Composer是必须的.

对于使用者来说Composer非常的简单,通过简单的一条命令将需要的代码包下载到vendor目录下,然后开发者就可以引入包并使用了.
其中的关键在于你项目定义的composer.json,可以定义项目需要依赖的包(可能有多个),而依赖的包可能又依赖其他的包(这就是组件的好处),这些都不用你烦心,Composer会自动下载你需要的一切,一切在于composer.json的定义.

Composer对于使用者来说是很透明,但是其背后的理念还是需要了解一下的,其的诞生也不是偶然的,得益于Github的快速发展,PHP语言也越来越现代化,显得更高大上了.

为了理解Composer,先大概了解下其结构:

Composer的结构

Composer命令行工具:
这个理解就比较简单了,通过使用者定义的Composer.json去下载你需要的代码,假如只是简单的使用Composer,那么掌握一些具体命令就完全可以了
Autoloading代码加载器:
通过Composer,开发者可以通过多种方式去使用,而其中的关键在于PHP的命名空间概念,以及PSR-4标准的发展,Composer只是根据这二者开发了一个代码自动加载器
Github:
有了Github,PHP开发人员可以将开源的代码托管在这上面,而Composer的发展源于Github,Composer本质上就是将Github上的代码下载到本地.
Packagist:
对于使用者来说使用的是Composer的命令行工具,那么命令行工具怎么知道有多少包可以被用户使用呢,这主要就是依赖于Packagist,Packagist是Composer主要的一个包信息存储库,包开发者将具体代码托管到Github上,将包信息提交到Packagist上,这样使用者就可以通过Composer去使用.
Composer根据本地定义的composer.json信息去查询Packagist,Packagist根据Composer.json/Package.json信息解析,最终对应到github仓库,Composer最终下载代码的时候还要依赖于Github仓库上的Composer.json,这里涉及到三种类型的composer.json,含义是不一样的.
Composer.json:
这是Composer的核心,是Composer的规则,上面也提到了三种类型的Composer.json,在使用的时候一定要注意区分,我初学的时候就总是搞乱.

Composer命令行工具

composer init
使用者可以在自己的项目下创建composer.json以便定义你项目的依赖包,也可以通过composer init交互式的创建composer.json.

composer install
应该是最常用的命令,composer会根据本地的composer.json安装包,将下载的包放入项目下的vendor目录下,同时将安装时候的包版本信息放入到composer.lock,以便锁定版本.
其实在install的时候,假如发现composer.lock版本和目前vendor目录下的代码版本是一致的,则Composer会什么也不做,composer.lock的目的就是让你安心在目前这个版本下工作,而不获取最新版本的包.

composer update
那么如何更新composer.lock以便获取到最新版本的包呢?通过这个命令即可更新最新版本的包

composer config
这个命令还是建议了解下,全局的配置保存在COMPOSER_HOME/config.json,非全局的配置信息则存储在本项目目录下.
composer config --list -g
composer config -g notify-on-install false
composer global config bin-dir --absolutecomposer create-project
这个命令不常用,但是个人觉得还是很重要的,使用普通的install命令是将项目所有的依赖包下载到本项目vendor目录下.而通过这个命令则是将所有的代码及其依赖的包放到一个目录下,相当于执行了一个git clone命令,一般是包的开发者可能为了修复bug会使用该命令.

composer global
这是一个全局的安装命令,它允许你在COMPOSER_HOME目录下执行Composer的命令,比如install,update.当然你的COMPOSER_HOME要在$PATH环境下.

比如执行composer global require fabpot/php-cs-fixer,现在php-cs-fixer命令行可以全局运行了,如果稍后想更新它,只需要运行composer global update

composer dump-autoload
当你修改项目下的composer.json的文件,并不一定要运行composer update命令进行更新,有的时候可以使用该命令来更新加载器,比如你要引用本地自定义的包(不是来自于packagist),后面会通过实践来说明该命令.

composer require
假如手动或者交互式创建composer.json文件,可以直接使用该命令来安装包composer require cerdic/css-tidy:1.5.2
composer require "ywdblog/phpcomposer:dev-master"–prefer-source和–prefer-dist参数
–prefer-dist:对于稳定的包来说,一般Composer安装默认使用该参数,这也能加快安装,比如有可能直接从packagist安装了相应的包,而不用实际去Github上下载包.
–prefer-source:假如使用该参数,则会直接从Github上安装,安装包后vendor目录下还含有.git信息
 
composer require "ywdblog/phpcomposer:dev-master" --prefer-source
#在vendor/ywdblog/phpcomposer目录下含有.git信息
 如何给Composer添加代理
在国内使用Composer下载特别慢,可以通过二个方法进行加速

composer config repo.packagist composer "https://packagist.phpcomposer.com"
编辑composer.json"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}Autoloading代码加载器

composer本身集成一个autoloader,支持PSR-4,PSR-0,classmap,files autoloading.

这里通过一个例子来说明通过Composer如何引用classmap,files,本地符合PSR-4标准的代码

编辑composer.json
"autoload": {
"classmap": ["othsrc/","classsrc.php"],
"files": ["othsrc/filesrc.php"],
"psr-4": {"Foo\\Bar\\": "src"}
}composer dump-autoload
 
通过上述的操作,对于PSR-4来说等同注册了一个PSR-4 autoloader(从Foo\Bar命名空间)
假如不想使用Composer的autoloader,可以直接包含vendor/composer/autoload_*.php 文件,配置自己的加载器.
具体的例子托管在github上,可参考.

Repositories

关于Repositories,了解其不是必须的,但是假如掌握则更能理解Composer,对于Repositories,其中文文档和英文文档解释的很好,这里也进行了一些摘抄.

基本概念
包:
Composer是一个依赖管理工具,它在本地安装一些资源包和包的描述(比如包名称和对应的版本),比较重要的元数据描述是dist和source,dist指向一个存档,该存档是对一个资源包的某个版本的数据进行的打包.source指向一个开发中的源,这通常是一个源代码仓库(比如git)

资源库:
一个资源库是一个包的来源.它是一个packages/versions的列表.
Composer将查看所有你定义的repositories以找到项目需要的资源包(这句话很重要).
默认情况下已经将Packagist.org注册到Composer(或者理解为Packagist.org是Composer资源库默认的仓库类型)

Composer资源库类型
Composer资源库包括四种类型,默认的是composer类型,也就是packagist.org所使用的资源类型.
它使用一个单一的packages.json文件,包含了所有的资源包元数据.当你将包发布到pckagist.org上,则默认系统会创建一个packages.json,不过我没有找到我的包对应的文件.

VCS资源库类型
假如你想构建一个私有的Composer私有资源库类型,可以使用该类型,这里举一个例子,比如你在自己项目的composer.json定义如下,则就可以使用对应的Github上的代码了.{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ywdblog/phpcomposer"
}
],
"require": {
"ywdblog/phpcomposer": "dev-master"
}
}当运行composer update的时候,Comoser实际上是从Github上下载包而不是从pckagist.org上下载.

另外假如需要使用Package资源库类型或者PEAR资源库类型,参考官方文档即可,一般在composer.json中定义name、version属性即可.

Composer.json

在本文上面也多次提到了composer.json,比如你希望使用第三方包则需要在本地定义composer.json,Composer安装第三方包后,也会在第三方包目录下发现composer.json,那么这二者都叫composer.json,有什么区别呢?理解这非常的重要.

假如你在自己的项目下面定义一个composer.json,则这个包称之为ROOT包,这个composer.json定义你项目需要的条件(比如你的项目可能依赖一个第三方包).

composer.json中有些属性只能被ROOT包使用,比如config属性只在ROOT包中生效.

一个资源包是不是ROOT包,取决于它的上下文,比如你git clone ywdblog/phpcomposer,则这时候本地phpcomposer目录就是ROOT包,假如你在本地phpcomposer目录下composer require ywdblog/phpcomposer,则这时候你的项目phpcomposer就是ROOT包.

了解composer-schema.json可参考该网址,Laravel作为一个成熟的框架,其定义的composer.json非常经典

关于包的版本
当使用者在本地配置composer.json的时候,可以指定需要包的特定版本,Composer支持从Github仓库中下载Tag或者分支下的包.

对于Github上的Tag来说,Packagist会创建对应包的版本,它符合X.Y.Z,vX.Y.Z,X.Y.Z-包类型,就是说Github上虽然只有一个特定版本的包,但Composer支持多种形式的引用方式,比如:composer require monolog/monolog 1.0.0-RC1
composer require monolog/monolog v1.0.0-RC1
composer require monolog/monolog 1.0.*
composer require monolog/monolog ~1.10
 对于Github上的分支来说,Packagist会创建对应包的版本,假如分支名看起来像一个版本,将创建{分支名}-dev的包版本号,如果分支名看起来不像一个版本号,它将会创建dev-{分支名}形式的版本号
 
composer require monolog/monolog master-dev
composer require monolog/monolog master.x-dev总结:
理解Composer,最重要的是实践,最后也能明白PSR-4和命名空间,也可以尝试将你的项目发布到pckagist.org上.


文/虞大胆(简书作者)
原文链接:http://www.jianshu.com/p/98c5b254a79e
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 查看全部
Composer是一个非常流行的PHP包依赖管理工具,已经取代PEAR包管理器,对于PHP开发者来说掌握Composer是必须的.

对于使用者来说Composer非常的简单,通过简单的一条命令将需要的代码包下载到vendor目录下,然后开发者就可以引入包并使用了.
其中的关键在于你项目定义的composer.json,可以定义项目需要依赖的包(可能有多个),而依赖的包可能又依赖其他的包(这就是组件的好处),这些都不用你烦心,Composer会自动下载你需要的一切,一切在于composer.json的定义.

Composer对于使用者来说是很透明,但是其背后的理念还是需要了解一下的,其的诞生也不是偶然的,得益于Github的快速发展,PHP语言也越来越现代化,显得更高大上了.

为了理解Composer,先大概了解下其结构:

Composer的结构

Composer命令行工具:
这个理解就比较简单了,通过使用者定义的Composer.json去下载你需要的代码,假如只是简单的使用Composer,那么掌握一些具体命令就完全可以了
Autoloading代码加载器:
通过Composer,开发者可以通过多种方式去使用,而其中的关键在于PHP的命名空间概念,以及PSR-4标准的发展,Composer只是根据这二者开发了一个代码自动加载器
Github:
有了Github,PHP开发人员可以将开源的代码托管在这上面,而Composer的发展源于Github,Composer本质上就是将Github上的代码下载到本地.
Packagist:
对于使用者来说使用的是Composer的命令行工具,那么命令行工具怎么知道有多少包可以被用户使用呢,这主要就是依赖于Packagist,Packagist是Composer主要的一个包信息存储库,包开发者将具体代码托管到Github上,将包信息提交到Packagist上,这样使用者就可以通过Composer去使用.
Composer根据本地定义的composer.json信息去查询Packagist,Packagist根据Composer.json/Package.json信息解析,最终对应到github仓库,Composer最终下载代码的时候还要依赖于Github仓库上的Composer.json,这里涉及到三种类型的composer.json,含义是不一样的.
Composer.json:
这是Composer的核心,是Composer的规则,上面也提到了三种类型的Composer.json,在使用的时候一定要注意区分,我初学的时候就总是搞乱.

Composer命令行工具

composer init
使用者可以在自己的项目下创建composer.json以便定义你项目的依赖包,也可以通过composer init交互式的创建composer.json.

composer install
应该是最常用的命令,composer会根据本地的composer.json安装包,将下载的包放入项目下的vendor目录下,同时将安装时候的包版本信息放入到composer.lock,以便锁定版本.
其实在install的时候,假如发现composer.lock版本和目前vendor目录下的代码版本是一致的,则Composer会什么也不做,composer.lock的目的就是让你安心在目前这个版本下工作,而不获取最新版本的包.

composer update
那么如何更新composer.lock以便获取到最新版本的包呢?通过这个命令即可更新最新版本的包

composer config
这个命令还是建议了解下,全局的配置保存在COMPOSER_HOME/config.json,非全局的配置信息则存储在本项目目录下.
composer config --list -g
composer config -g notify-on-install false
composer global config bin-dir --absolute
composer create-project
这个命令不常用,但是个人觉得还是很重要的,使用普通的install命令是将项目所有的依赖包下载到本项目vendor目录下.而通过这个命令则是将所有的代码及其依赖的包放到一个目录下,相当于执行了一个git clone命令,一般是包的开发者可能为了修复bug会使用该命令.

composer global
这是一个全局的安装命令,它允许你在COMPOSER_HOME目录下执行Composer的命令,比如install,update.当然你的COMPOSER_HOME要在$PATH环境下.

比如执行composer global require fabpot/php-cs-fixer,现在php-cs-fixer命令行可以全局运行了,如果稍后想更新它,只需要运行composer global update

composer dump-autoload
当你修改项目下的composer.json的文件,并不一定要运行composer update命令进行更新,有的时候可以使用该命令来更新加载器,比如你要引用本地自定义的包(不是来自于packagist),后面会通过实践来说明该命令.

composer require
假如手动或者交互式创建composer.json文件,可以直接使用该命令来安装包
composer require  cerdic/css-tidy:1.5.2
composer require "ywdblog/phpcomposer:dev-master"
–prefer-source和–prefer-dist参数
–prefer-dist:对于稳定的包来说,一般Composer安装默认使用该参数,这也能加快安装,比如有可能直接从packagist安装了相应的包,而不用实际去Github上下载包.
–prefer-source:假如使用该参数,则会直接从Github上安装,安装包后vendor目录下还含有.git信息
 
composer require "ywdblog/phpcomposer:dev-master" --prefer-source 
#在vendor/ywdblog/phpcomposer目录下含有.git信息

 如何给Composer添加代理
在国内使用Composer下载特别慢,可以通过二个方法进行加速

composer config repo.packagist composer "https://packagist.phpcomposer.com"
编辑composer.json
"repositories": {
"packagist": {
"type": "composer",
"url": "https://packagist.phpcomposer.com"
}
}
Autoloading代码加载器

composer本身集成一个autoloader,支持PSR-4,PSR-0,classmap,files autoloading.

这里通过一个例子来说明通过Composer如何引用classmap,files,本地符合PSR-4标准的代码

编辑composer.json
"autoload": {
"classmap": ["othsrc/","classsrc.php"],
"files": ["othsrc/filesrc.php"],
"psr-4": {"Foo\\Bar\\": "src"}
}
composer dump-autoload
 
通过上述的操作,对于PSR-4来说等同注册了一个PSR-4 autoloader(从Foo\Bar命名空间)
假如不想使用Composer的autoloader,可以直接包含vendor/composer/autoload_*.php 文件,配置自己的加载器.
具体的例子托管在github上,可参考.

Repositories

关于Repositories,了解其不是必须的,但是假如掌握则更能理解Composer,对于Repositories,其中文文档和英文文档解释的很好,这里也进行了一些摘抄.

基本概念
包:
Composer是一个依赖管理工具,它在本地安装一些资源包和包的描述(比如包名称和对应的版本),比较重要的元数据描述是dist和source,dist指向一个存档,该存档是对一个资源包的某个版本的数据进行的打包.source指向一个开发中的源,这通常是一个源代码仓库(比如git)

资源库:
一个资源库是一个包的来源.它是一个packages/versions的列表.
Composer将查看所有你定义的repositories以找到项目需要的资源包(这句话很重要).
默认情况下已经将Packagist.org注册到Composer(或者理解为Packagist.org是Composer资源库默认的仓库类型)

Composer资源库类型
Composer资源库包括四种类型,默认的是composer类型,也就是packagist.org所使用的资源类型.
它使用一个单一的packages.json文件,包含了所有的资源包元数据.当你将包发布到pckagist.org上,则默认系统会创建一个packages.json,不过我没有找到我的包对应的文件.

VCS资源库类型
假如你想构建一个私有的Composer私有资源库类型,可以使用该类型,这里举一个例子,比如你在自己项目的composer.json定义如下,则就可以使用对应的Github上的代码了.
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/ywdblog/phpcomposer"
}
],
"require": {
"ywdblog/phpcomposer": "dev-master"
}
}
当运行composer update的时候,Comoser实际上是从Github上下载包而不是从pckagist.org上下载.

另外假如需要使用Package资源库类型或者PEAR资源库类型,参考官方文档即可,一般在composer.json中定义name、version属性即可.

Composer.json

在本文上面也多次提到了composer.json,比如你希望使用第三方包则需要在本地定义composer.json,Composer安装第三方包后,也会在第三方包目录下发现composer.json,那么这二者都叫composer.json,有什么区别呢?理解这非常的重要.

假如你在自己的项目下面定义一个composer.json,则这个包称之为ROOT包,这个composer.json定义你项目需要的条件(比如你的项目可能依赖一个第三方包).

composer.json中有些属性只能被ROOT包使用,比如config属性只在ROOT包中生效.

一个资源包是不是ROOT包,取决于它的上下文,比如你git clone ywdblog/phpcomposer,则这时候本地phpcomposer目录就是ROOT包,假如你在本地phpcomposer目录下composer require ywdblog/phpcomposer,则这时候你的项目phpcomposer就是ROOT包.

了解composer-schema.json可参考该网址,Laravel作为一个成熟的框架,其定义的composer.json非常经典

关于包的版本
当使用者在本地配置composer.json的时候,可以指定需要包的特定版本,Composer支持从Github仓库中下载Tag或者分支下的包.

对于Github上的Tag来说,Packagist会创建对应包的版本,它符合X.Y.Z,vX.Y.Z,X.Y.Z-包类型,就是说Github上虽然只有一个特定版本的包,但Composer支持多种形式的引用方式,比如:
composer require monolog/monolog  1.0.0-RC1 
composer require monolog/monolog v1.0.0-RC1
composer require monolog/monolog 1.0.*
composer require monolog/monolog ~1.10

 对于Github上的分支来说,Packagist会创建对应包的版本,假如分支名看起来像一个版本,将创建{分支名}-dev的包版本号,如果分支名看起来不像一个版本号,它将会创建dev-{分支名}形式的版本号
 
composer require monolog/monolog  master-dev
composer require monolog/monolog master.x-dev
总结:
理解Composer,最重要的是实践,最后也能明白PSR-4和命名空间,也可以尝试将你的项目发布到pckagist.org上.


文/虞大胆(简书作者)
原文链接:http://www.jianshu.com/p/98c5b254a79e
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

消息队列(Message Queue)学习贴

zkbhj 发表了文章 • 1 个评论 • 1222 次浏览 • 2016-12-17 14:54 • 来自相关话题

1、什么是消息队列?
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
 2、消息队列应用场景有哪些?
大致会有异步处理,应用解耦,流量削锋和消息通讯四个场景。
 
2.1异步处理

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。





 
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。





 
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是1000次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
 
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
 





 
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
 
2.2应用解耦

场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:





 
传统模式的缺点:

1)  假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;

2)  订单系统与库存系统耦合;

如何解决以上问题呢?引入应用消息队列后的方案,如下图:
 





订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。
 
2.3流量削锋

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数;可以缓解短时间内高流量压垮应用;





用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;秒杀业务根据消息队列中的请求信息,再做后续处理。
 
2.4日志处理

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:





日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;Kafka消息队列,负责日志数据的接收,存储和转发;日志处理应用:订阅并消费kafka队列中的日志数据;
 
2.5消息通讯

消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
 
点对点通讯:





 
客户端A和客户端B使用同一队列,进行消息通讯。

聊天室通讯:





 
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。

以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
 
3、消息模型
有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
 
3.1.1 P2P模式





 
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

P2P的特点
每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列接收者在成功接收消息之后需向队列应答成功

如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。
 
3.1.2 Pub/sub模式





 
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点
每个消息可以有多个消费者发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。为了消费消息,订阅者必须保持运行的状态。

为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。 
参考文章:
http://kb.cnblogs.com/page/537914/
http://blog.csdn.net/shaobingj126/article/details/50585035
  查看全部
1、什么是消息队列?
消息队列(Message Queue,简称MQ),从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已。其主要用途:不同进程Process/线程Thread之间通信。
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
 2、消息队列应用场景有哪些?
大致会有异步处理,应用解耦,流量削锋和消息通讯四个场景。
 
2.1异步处理

场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。

820332-20160124211106000-2080222350.png

 
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。

820332-20160124211115703-218873208.png

 
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。

因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是1000次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
 
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
 

820332-20160124211131625-1083908699.png

 
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
 
2.2应用解耦

场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:

820332-20160124211254187-1511483255.png

 
传统模式的缺点:

1)  假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;

2)  订单系统与库存系统耦合;

如何解决以上问题呢?引入应用消息队列后的方案,如下图:
 

820332-20160124211307687-1914946501.png

  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
  • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
  • 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。

 
2.3流量削锋

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。

应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
  1. 可以控制活动的人数;
  2. 可以缓解短时间内高流量压垮应用;


820332-20160124211333125-923847962.png

  1. 用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
  2. 秒杀业务根据消息队列中的请求信息,再做后续处理。

 
2.4日志处理

日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:

820332-20160124211436718-1054529852.png

  • 日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;
  • Kafka消息队列,负责日志数据的接收,存储和转发;
  • 日志处理应用:订阅并消费kafka队列中的日志数据;

 
2.5消息通讯

消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
 
点对点通讯:

820332-20160124211500718-1411703435.png

 
客户端A和客户端B使用同一队列,进行消息通讯。

聊天室通讯:

820332-20160124211511859-1166529202.png

 
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。

以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
 
3、消息模型
有两种消息模型P2P(Point to Point),Publish/Subscribe(Pub/Sub)。
 
3.1.1 P2P模式

820332-20160124221143281-46837068.png

 
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。

P2P的特点
  • 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
  • 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
  • 接收者在成功接收消息之后需向队列应答成功


如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。
 
3.1.2 Pub/sub模式

820332-20160124221155968-1666724216.png

 
包含三个角色主题(Topic),发布者(Publisher),订阅者(Subscriber) 。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点
  • 每个消息可以有多个消费者
  • 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
  • 为了消费消息,订阅者必须保持运行的状态。


为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。

如果希望发送的消息可以不被做任何处理、或者只被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。 
参考文章:
http://kb.cnblogs.com/page/537914/
http://blog.csdn.net/shaobingj126/article/details/50585035
 

什么是SPA (单页应用程序)?

回复

zkbhj 回复了问题 • 1 人关注 • 1 个回复 • 2278 次浏览 • 2016-12-15 11:41 • 来自相关话题