豪翔天下

Change My World by Program

0%

supervisor是使用Python编写的进程管理软件,在实际开发中,一般用它来同时开始一批相关的进程,无论是Django的runserver还是直接管理Nginx、Apache等,都比较方便,这里是其使用方法:

安装supervisor

1
2
3
4
5
6
7
# ubuntu
apt-get install supervisor
service supervisor restart

# centos
yum install supervisor
/etc/init.d/supervisord restart
阅读全文 »

Tornado

中文文档
是不是很神奇,我居然会学习除Django以外的另一种Python Web框架。但事实确实如此,我学了,并且用了一下,感觉和Django确实存在很大的不同的。
学这个的主要原因是要参加饿了么的黑客马拉松,主题是使用Python或Java或Node设计Resutful API,来应对高并发的请求,保证数据的一致性。虽然最后的分数并不理想,但是在这次比赛中学到了很多的东西,Tornado就是其中之一。
以前只听说过Tornado比Django拥有更高的并发和异步特性,只听过Django坚持自己造轮子,其他语言的Web框架也只用过PHP的Laravel,当安装完Tornado后完全不知道该怎么玩儿,怎么着玩意儿自己不会生成一个项目,项目的框架还要自己写。后来才体会到,这样的好处,简洁、直观、轻便,它只提供最基础的东西,其他一切问题都由你自己来解决,同时,短短几千行的源代码,在遇到问题的时候,我们完全可以直接看源代码来查找问题出在哪儿,这就是简洁带来的好处。另外异步的作用也只有在实际需要的时候才能体会出来。由于我从来没写过异步的代码,所以,只是按照之前写代码那样,而没有根据异步的特性来实现,异步的代码是要在会阻塞的地方使用回调函数来实现,所以,我的程序并不能得高分。不过后来才知道Tornado通过gen将同步代码转换为异步的实现。

要想自己定义代码结构,可以参考qiwsir/TornadoWheel

阅读全文 »

原文链接:http://ju.outofmemory.cn/entry/108566

fuck,最讨厌java了,有同事说JKS是Java特有的东西,所以必须调用Java才能使用,but,I use Python,nothing is im
possible,发现使用requests来直接调用其它格式的证书文件就行,当然,Python也可以用pyjks包来将jks转换为其它格式,但没必要那样做,
因为直接用ssl工具转换就可以一劳永逸了。

JKS(Java
KeyStore)是Java的一个证书仓库,包括授权整数和公钥整数等。JDK提供了一个工具keytool用于管理keystore。转换步骤:

  1. 使用keytool导出成PKCS12格式:

    keytool -importkeystore -srckeystore server.jks -destkeystore server.p12 -srcstoretype jks -deststoretype pkcs12

    输入目标密钥库口令:

再次输入新口令:
输入源密钥库口令:  

已成功导入别名 ca_root 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消
  1. 生成pem证书(包含了key,server证书和ca证书):

    生成key 加密的pem证书

    $ openssl pkcs12 -in server.p12 -out server.pem
    Enter Import Password:
    MAC verified OK
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:

# 生成key 非加密的pem证书




$ openssl pkcs12 -nodes -in server.p12 -out server.pem
Enter Import Password:
MAC verified OK
  1. 单独导出key:

    生成加密的key

    $ openssl pkcs12 -in tankywoo.p12 -nocerts -out server.key
    Enter Import Password:
    MAC verified OK
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:

# 生成非加密的key




$ openssl pkcs12 -in tankywoo.p12 -nocerts -nodes -out server.key
Enter Import Password:
MAC verified OK
  1. 单独导出server证书:

    $ openssl pkcs12 -in server.p12 -nokeys -clcerts -out server.crt
    Enter Import Password:
    MAC verified OK

  2. 单独导出ca证书:

    $ openssl pkcs12 -in server.p12 -nokeys -cacerts -out ca.crt
    Enter Import Password:
    MAC verified OK

TroubleShooting:

1.至于原文中出现的导入ca_root证书出现错误,它那个方法貌似不管用,这里建议将Java升级到Java8即可成功导入。

2.在Python中使用ssl时(无论是用httplib、ssl还是requests),可能出现以下错误:

Traceback (most recent call last):
  File "client.py", line 10, in <module>
    ssl_sock.connect(('', 9000))
  File "/Users/amk/source/p/python/Lib/ssl.py", line 204, in connect
    self.ca_certs)
ssl.SSLError: [Errno 0] _ssl.c:327: error:00000000:lib(0):func(0):reason(0)

根本原因就是提供的证书是错误的

原文地址:http://coolshell.cn/articles/10822.html

本篇文章写于2013年底,而今天我看来,依然是精华中的精华,就喜欢这种深入浅出的文章,带我们对函数式编程更深入的理解,并且本篇文章采用多种语言多种角度来
向我们讲解了到底什么才是函数式编程,再加上最近工作上很多的问题,才发现,其实公司之前的代码有很多优秀的地方。

原文地址:http://coolshell.cn/articles/10822.html

本篇文章写于2013年底,而今天我看来,依然是精华中的精华,就喜欢这种深入浅出的文章,带我们对函数式编程更深入的理解,并且本篇文章采用多种语言多种角度来
向我们讲解了到底什么才是函数式编程,再加上最近工作上很多的问题,才发现,其实公司之前的代码有很多优秀的地方。

当我们说起函数式编程来说,我们会看到如下函数式编程的长相:

  • 函数式编程的三大特性:

  • *immutable data 不可变数据**:像Clojure一样,默认上变量是不可变的,如果你要改变变量,你需要把变量copy出去修改。这样一来,可以让你的程序少很多Bug。因为,程序中的状态不好维护,在并发的时候更不好维护。(你可以试想一下如果你的程序有个复杂的状态,当以后别人改你代码的时候,是很容易出bug的,在并行中这样的问题就更多了)

  • *first class functions**:这个技术可以让你的函数就像变量一样来使用。也就是说,你的函数可以像变量一样被创建,修改,并当成变量一样传递,返回或是在函数中嵌套函数。这个有点像Javascript的Prototype(参看Javascript的面向对象编程

  • *尾递归优化**:我们知道递归的害处,那就是如果递归很深的话,stack受不了,并会导致性能大幅度下降。所以,我们使用尾递归优化技术——每次递归时都会重用stack,这样一来能够提升性能,当然,这需要语言或编译器的支持。Python就不支持。

    • 函数式编程的几个技术
  • map & reduce* :这个技术不用多说了,函数式编程最常见的技术就是对一个集合做Map和Reduce操作。这比起过程式的语言来说,在代码上要更容易阅读。(传统过程式的语言需要使用for/while循环,然后在各种变量中把数据倒过来倒过去的)这个很像C++中的STL中的foreach,find_if,count_if之流的函数的玩法。

  • *pipeline**:这个技术的意思是,把函数实例成一个一个的action,然后,把一组action放到一个数组或是列表中,然后把数据传给这个action list,数据就像一个pipeline一样顺序地被各个函数所操作,最终得到我们想要的结果。

  • recursing 递归* :递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。

  • *currying**:把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。在C++中,这个很像STL中的bind_1st或是bind2nd。

  • *higher order function 高阶函数**:所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。

    • 还有函数式的一些好处
  • *parallelization 并行:所谓并行的意思就是在并行环境下,各个线程之间不需要同步或互斥。lazy evaluation 惰性求值:这个需要编译器的支持。表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值,也就是说,语句如x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式被计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。determinism 确定性**:所谓确定性的意思就是像数学那样 f(x) = y ,这个函数无论在什么场景下,都会得到同样的结果,这个我们称之为函数的确定性。而不是像程序中的很多函数那样,同一个参数,却会在不同的场景下计算出不同的结果。所谓不同的场景的意思就是我们的函数会根据一些运行中的状态信息的不同而发生变化。

上面的那些东西太抽象了,还是让我们来循序渐近地看一些例子吧。

我们先用一个最简单的例子来说明一下什么是函数式编程。

先看一个非函数式的例子:

1

2

3

4

|

int cnt;

void increment(){

cnt++;

}

—|—

那么,函数式的应该怎么写呢?

1

2

3

|

int increment(int cnt){

return cnt+1;

}

—|—

你可能会觉得这个例子太普通了。是的,这个例子就是函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你

我们再来看一个简单例子:

1

2

3

4

5

6

7

8

9

10

|

def inc(x):

def incx(y):

return x+y

return incx

inc2 = inc(2)

inc5 = inc(5)

print inc2(5) # 输出 7

print inc5(5) # 输出 10

—|—

我们可以看到上面那个例子inc()函数返回了另一个函数incx(),于是我们可以用inc()函数来构造各种版本的inc函数,比如:inc2()和inc5()
。这个技术其实就是上面所说的Currying技术。从这个技术上,你可能体会到函数式编程的理念:把函数当成变量来用,关注于描述问题而不是怎么实现,这样
可以让代码更易读。

Map & Reduce

在函数式编程中,我们不应该用循环迭代的方式,我们应该用更为高级的方法,如下所示的Python代码

1

2

3

|

name_len = map(len, [“hao”, “chen”, “coolshell”])

print name_len

输出 [3, 4, 9]

—|—

你可以看到这样的代码很易读,因为,这样的代码是在描述要干什么,而不是怎么干

我们再来看一个Python代码的例子:

1

2

3

4

5

6

|

def toUpper(item):

return item.upper()

upper_name = map(toUpper, [“hao”, “chen”, “coolshell”])

print upper_name

输出 [‘HAO’, ‘CHEN’, ‘COOLSHELL’]

—|—

顺便说一下,上面的例子个是不是和我们的STL的transform有些像?

1

2

3

4

5

6

7

8

9

10

11

12

|

#include

#include

#include

using namespace std;

int main() {

string s=”hello”;

string out;

transform(s.begin(), s.end(), back_inserter(out), ::toupper);

cout << out << endl;

// 输出:HELLO

}

—|—

在上面Python的那个例子中我们可以看到,我们写义了一个函数toUpper,这个函数没有改变传进来的值,只是把传进来的值做个简单的操作,然后返回。然后,我
们把其用在map函数中,就可以很清楚地描述出我们想要干什么。而不会去理解一个在循环中的怎么实现的代码,最终在读了很多循环的逻辑后才发现原来是这个或那个意思。
下面,我们看看描述实现方法的过程式编程是怎么玩的(看上去是不是不如函数式的清晰?):

1

2

3

4

|

upname =[‘HAO’, ‘CHEN’, ‘COOLSHELL’]

lowname =[]

for i in range(len(upname)):

lowname.append( upname[i].lower() )

—|—

对于map我们别忘了lambda表达式:你可以简单地理解为这是一个inline的匿名函数。下面的lambda表达式相当于:def func(x):
return x*x

1

2

3

|

squares = map(lambda x: x * x, range(9))

print squares

输出 [0, 1, 4, 9, 16, 25, 36, 49, 64]

—|—

我们再来看看reduce怎么玩?(下面的lambda表达式中有两个参数,也就是说每次从列表中取两个值,计算结果后把这个值再放回去,下面的表达式相当于:(((
(1+2)+3)+4)+5) )

1

2

|

print reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])

输出 15

—|—

Python中的除了map和reduce外,还有一些别的如filter, find, all,
any的函数做辅助(其它函数式的语言也有),可以让你的代码更简洁,更易读。 我们再来看一个比较复杂的例子:

计算数组中正数的平均值

1

2

3

4

5

6

7

8

9

10

11

12

13

|

num =[2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8]

positive_num_cnt = 0

positive_num_sum = 0

for i in range(len(num)):

if num[i] > 0:

positive_num_cnt += 1

positive_num_sum += num[i]

if positive_num_cnt > 0:

average = positive_num_sum / positive_num_cnt

print average

输出 5

—|—

如果用函数式编程,这个例子可以写成这样:

1

2

|

positive_num = filter(lambda x: x>0, num)

average = reduce(lambda x,y: x+y, positive_num) / len( positive_num )

—|—

C++11玩的法:

1

2

3

4

5

6

7

8

9

10

11

12

|

#include

#include

#include

#include

#include

using namespace std;

vector num {2, -5, 9, 7, -2, 5, 3, 1, 0, -3, 8};

vector p_num;

copy_if(num.begin(), num.end(<span class=”crayon-sy” sty

原文地址:<https://www.digitalocean.com/community/tutorials/apache-vs-nginx-
practical-considerations>

总之就是各有各的优点,最好的方式就是nginx在前面做反向代理,并顺便处理静态内容,而apache则负责处理动态内容。

Introduction

Apache and Nginx are the two most common open source web servers in the world.
Together, they are responsible for serving over 50% of traffic on the
internet. Both solutions are capable of handling diverse workloads and working
with other software to provide a complete web stack.

While Apache and Nginx share many qualities, they should not be thought of as
entirely interchangeable. Each excels in its own way and it is important to
understand the situations where you may need to reevaluate your web server of
choice. This article will be devoted to a discussion of how each server stacks
up in various areas.

General Overview

Before we dive into the differences between Apache and Nginx, let’s take a
quick look at the background of these two projects and their general
characteristics.

Apache

The Apache HTTP Server was created by Robert McCool in 1995 and has been
developed under the direction of the Apache Software Foundation since 1999.
Since the HTTP web server is the foundation’s original project and is by far
their most popular piece of software, it is often referred to simply as
“Apache”.

The Apache web server has been the most popular server on the internet since
1996. Because of this popularity, Apache benefits from great documentation and
integrated support from other software projects.

Apache is often chosen by administrators for its flexibility, power, and
widespread support. It is extensible through a dynamically loadable module
system and can process a large number of interpreted languages without
connecting out to separate software.

Nginx

In 2002, Igor Sysoev began work on Nginx as an answer to the C10K problem,
which was a challenge for web servers to begin handling ten thousand
concurrent connections as a requirement for the modern web. The initial public
release was made in 2004, meeting this goal by relying on an asynchronous(异步),
events-driven(事件驱动) architecture.

Nginx has grown in popularity since its release due to its light-weight
resource utilization and its ability to scale easily on minimal hardware.
Nginx excels at serving static content(静态内容) quickly and is designed to pass
dynamic requests off to other software that is better suited for those
purposes.

Nginx is often selected by administrators for its resource efficiency and
responsiveness under load. Advocates welcome Nginx’s focus on core web server
and proxy features.

Connection Handling Architecturel(连接处理架构)

One big difference between Apache and Nginx is the actual way that they handle
connections and traffic. This provides perhaps the most significant difference
in the way that they respond to different traffic conditions.

Apache

Apache provides a variety of multi-processing modules (Apache calls these
MPMs) that dictate(决定) how client requests are handled. Basically, this allows
administrators to swap out its connection handling architecture easily. These
are:

  • mpm_prefork: This processing module spawns(产生) processes with a single thread each to handle request(一个请求一个线程). Each child can handle a single connection at a time. As long as the number of requests is fewer than the number of processes, this MPM is very fast(请求数量比进程数量少的时候会很快). However, performance degrades quickly after the requests surpass the number of processes, so this is not a good choice in many scenarios. Each process has a significant impact on RAM consumption, so this MPM is difficult to scale effectively. This may still be a good choice though if used in conjunction with other components that are not built with threads in mind. For instance, PHP is not thread-safe, so this MPM is recommended as the only safe way of working with mod_php, the Apache module for processing these files.
  • mpm_worker: This module spawns processes that can each manage multiple threads(每个进程可以管理多个线程). Each of these threads can handle a single connection. Threads are much more efficient than processes(线程比进程更高效), which means that this MPM scales better than the prefork MPM. Since there are more threads than processes, this also means that new connections can immediately take a free thread instead of having to wait for a free process(处理新的连接只需要有新的线程而不需要等待进程释放).
  • mpm_event: This module is similar to the worker module in most situations, but is optimized to handle keep-alive connections(对持久连接进行了优化). When using the worker MPM, a connection will hold a thread regardless of whether a request is actively being made for as long as the connection is kept alive. The event MPM handles keep alive connections by setting aside dedicated(专用的) threads for handling keep alive connections and passing active requests off to other threads. This keeps the module from getting bogged down by keep-alive requests, allowing for faster execution. This was marked stable with the release of Apache 2.4. As you can see, Apache provides a flexible architecture for choosing different connection and request handling algorithms. The choices provided are mainly a function of the server’s evolution and the increasing need for concurrency as the internet landscape has changed.

Nginx

Nginx came onto the scene after Apache, with more awareness of the concurrency
problems that would face sites at scale(更注重并发问题). Leveraging(利用) this
knowledge, Nginx was designed from the ground up to use an asynchronous, non-
blocking, event-driven connection handling algorithm.

Nginx spawns worker processes, each of which can handle thousands of
connections(产生的是worker进程,每个都可以处理上千个连接). The worker processes accomplish this
by implementing a fast looping mechanism(快速的循环机制) that continuously checks for
and processes events. Decoupling(解耦) actual work from connections allows each
worker to concern itself with a connection only when a new event has been
triggered.

Each of the connections handled by the worker are placed within the event loop
where they exist with other connections. Within the loop, events are processed
asynchronously, allowing work to be handled in a non-blocking manner. When the
connection closes, it is removed from the loop.

This style of connection processing allows Nginx to scale incredibly far with
limited resources. Since the server is single-threaded and processes are not
spawned to handle each new connection, the memory and CPU usage tends to stay
relatively consistent, even at times of heavy load.

Static vs Dynamic Content

In terms of real world use-cases, one of the most common comparisons between
Apache and Nginx is the way in which each server handles requests for static
and dynamic content.

Apache

Apache servers can handle static content using its conventional file-based
methods. The performance of these operations is mainly a function of the MPM
methods described above.

Apache can also process dynamic content by embedding(嵌入) a processor of the
language in question into each of its worker instances. This allows it to
execute dynamic content within the web server itself without having to rely on
external components. These dynamic processors can be enabled through the use
of dynamically loadable modules.

Apache’s ability to handle dynamic content internally means that configuration
of dynamic processing tends to be simpler. Communication does not need to be
coordinated with an additional piece of software and modules can easily be
swapped out if the content requirements change.

Nginx

Nginx does not have any ability to process dynamic content
natively(本身无法处理动态内容). To handle PHP and other requests for dynamic content,
Nginx must pass to an external processor for execution and wait for the
rendered content to be sent back(必须使用外部的执行程序然后等待返回). The results can then be
relayed to the client.

For administrators, this means that communication must be configured between
Nginx and the processor over one of the protocols Nginx knows how to speak
(http, FastCGI, SCGI, uWSGI, memcache). This can complicate things slightly,
especially when trying to anticipate the number of connections to allow, as an
additional connection will be used for each call to the processor.

However, this method has some advantages as well. Since the dynamic
interpreter is not embedded in the worker process, its overhead will only be
present for dynamic content. Static content can be served in a straight-
forward manner and the interpreter will only be contacted when needed. Apache
can also function in this manner, but doing so removes the benefits in the
previous section.

Distributed vs Centralized Configuration

For administrators, one of the most readily apparent differences between these
two pieces of software is whether directory-level configuration is permitted
within the content directories.

Apache

Apache includes an option to allow additional configuration on a per-directory
basis by inspecting and interpreting directives in hidden files within the
content directories themselves. These files are known as .htaccess
files.(可以管理每个目录)

Since these files reside within the content directories themselves, when
handling a request, Apache checks each component of the path to the requested
file for an .htaccess file and applies the directives found within. This
effectively allows decentralized configuration of the web server, which is
often used for implementing URL rewrites, access restrictions, authorization
and authentication, even caching policies.

While the above examples can all be configured in the main Apache
configuration file,.htaccess files have some important advantages. First,
since these are interpreted each time they are found along a request path,
they are implemented immediately without reloading the server. Second, it
makes it possible to allow non-privileged users to control certain aspects of
their own web content without giving them control over the entire
configuration file.

This provides an easy way for certain web software, like content management
systems, to configure their environment without providing access to the
central configuration file. This is also used by shared hosting providers to
retain control of the main configuration while giving clients control over
their specific directories.

Nginx

Nginx does not interpret .htaccess files, nor does it provide any mechanism
for evaluating per-directory configuration outside of the main configuration
file. This may be less flexible than the Apache model, but it does have its
own advantages.

The most notable improvement over the .htaccess system of directory-level
configuration is increased performance. For a typical Apache setup that may
allow .htaccess in any directory, the server will check for these files in
each of the parent directories leading up to the requested file, for each
request. If one or more .htaccess files are found during this search, they
must be read and interpreted. By not allowing directory overrides, Nginx can
serve requests faster by doing a single directory lookup and file read for
each request (assuming that the file is found in the conventional directory
structure).

Another advantage is security related(安全). Distributing directory-level
configuration access also distributes the responsibility of security to
individual users, who may not be trusted to handle this task well. Ensuring
that the administrator maintains control over the entire web server can
prevent some security missteps that may occur when access is given to other
parties.

Keep in mind that it is possible to turn off .htaccess interpretation in
Apache if these concerns resonate with you.

File vs URI-Based Interpretation

How the web server interprets requests and maps them to actual resources on
the system is another area where these two servers differ.

Apache

Apache provides the ability to interpret a request as a physical resource on
the filesystem or as a URI location that may need a more abstract evaluation.
In general, for the former Apache uses or blocks, while it
utilizes blocks for more abstract resources.

Because Apache was designed from the ground up as a web server, the default is
usually to interpret requests as filesystem resources. It begins by taking the
document root and appending the portion of the request following the host and
port number to try to find an actual file. Basically, the filesystem hierarchy
is represented on the web as the available document tree.

Apache provides a number of alternatives for when the request does not match
the underlying filesystem. For instance, an Alias directive can be used to map
to an alternative location. Using blocks is a method of working
with the URI itself instead of the filesystem. There are also regular
expression variants which can be used to apply configuration more flexibly
throughout the filesystem.

While Apache has the ability to operate on both the underlying filesystem and
the webspace, it leans heavily towards filesystem methods. This can be seen in
some of the design decisions, including the use of .htaccess files for per-
directory configuration. The Apache docs themselves warn against using URI-
based blocks to restrict access when the request mirrors the underlying
filesystem.

Nginx

Nginx was created to be both a web server and a proxy server. Due to the
architecture required for these two roles, it works primarily with URIs,
translating to the filesystem when necessary.

This can be seen in some of the ways that Nginx configuration files are
constructed and interpreted.Nginx does not provide a mechanism for specifying
configuration for a filesystem directory and instead parses the URI itself.

For instance, the primary configuration blocks for Nginx are server and
location blocks. The server block interprets the host being requested, while
the location blocks are responsible for matching portions of the URI that
comes after the host and port. At this point, the request is being interpreted
as a URI, not as a location on the filesystem.

For static files, all requests eventually have to be mapped to a location on
the filesystem. First, Nginx selects the server and location blocks that will
handle the request and then combines the document root with the URI, adapting
anything necessary according to the configuration specified.

This may seem similar, but parsing requests primarily as URIs instead of
filesystem locations allows Nginx to more easily function in both web, mail,
and proxy server roles. Nginx is configured simply by laying out how to
respond to different request patterns. Nginx does not check the filesystem
until it is ready to serve the request, which explains why it does not
implement a form of .htaccess files.

Modules

Both Nginx and Apache are extensible through module systems, but the way that
they work differ significantly.

Apache

Apache’s module system allows you to dynamically load or unload modules to
satisfy your needs during the course of running the server. The Apache core is
always present, while modules can be turned on or off, adding or removing
additional functionality and hooking into the main server.

Apache uses this functionality for a large variety tasks. Due to the maturity
of the platform, there is an extensive library of modules available. These can
be used to alter some of the core functionality of the server, such as
mod_php, which embeds a PHP interpreter into each running worker.

Modules are not limited to processing dynamic content, however. Among other
functions, they can be used for rewriting URLs, authenticating clients,
hardening the server, logging, caching, compression, proxying, rate limiting,
and encrypting. Dynamic modules can extend the core functionality considerably
without much additional work.

Nginx

Nginx also implements a module system, but it is quite different from the
Apache system. In Nginx, modules are not dynamically loadable, so they must be
selected and compiled into the core software(模块居然不是动态加载的).

For many users, this will make Nginx much less flexible. This is especially
true for users who are not comfortable maintaining their own compiled software
outside of their distribution’s conventional packaging system. While
distributions’ packages tend to include the most commonly used modules, if you
require a non-standard module, you will have to build the server from source
yourself.

Nginx modules are still very useful though, and they allow you to dictate what
you want out of your server by only including the functionality you intend to
use. Some users also may consider this more secure, as arbitrary components
cannot be hooked into the server. However, if your server is ever put in a
position where this is possible, it is likely compromised already.

Nginx modules allow many of the same capabilities as Apache modules. For
instance, Nginx modules can provide proxying support, compression, rate
limiting, logging, rewriting, geolocation, authentication, encryption,
streaming, and mail functionality.

Support, Compatibility, Ecosystem, and Documentation

A major point to consider is what the actual process of getting up and running
will be given the landscape of available help and support among other
software.

Apache

Because Apache has been popular for so long, support for the server is fairly
ubiquitous. There is a large library of first- and third-party documentation
available for the core server and for task-based scenarios involving hooking
Apache up with other software.

Along with documentation, many tools and web projects include tools to
bootstrap themselves within an Apache environment. This may be included in the
projects themselves, or in the packages maintained by your distribution’s
packaging team.

Apache, in general, will have more support from third-party projects simply
because of its market share and the length of time it has been available.
Administrators are also somewhat more likely to have experience working with
Apache not only due to its prevalence, but also because many people start off
in shared-hosting scenarios which almost exclusively rely on Apache due to the
.htaccess distributed management capabilities.

Nginx

Nginx is experiencing increased support as more users adopt it for its
performance profile, but it still has some catching up to do in some key
areas.

In the past, it was difficult to find comprehensive English-language
documentation regarding Nginx due to the fact that most of the early
development and documentation were in Russian. As interest in the project
grew, the documentation has been filled out and there are now plenty of
administration resources on the Nginx site and through third parties.

In regards to third-party applications, support and documentation is becoming
more readily available, and package maintainers are beginning, in some cases,
to give choices between auto-configuring for Apache and Nginx. Even without
support, configuring Nginx to work with alternative software is usually
straight-forward so long as the project itself documents its requirements
(permissions, headers, etc).

Using Apache and Nginx Together

After going over the benefits and limitations of both Apache and Nginx, you
may have a better idea of which server is more suited to your needs. However,
many users find that it is possible to leverage each server’s strengths by
using them together.

The conventional configuration for this partnership is to place Nginx in front
of Apache as a reverse proxy(Nginx做反向代理). This will allow Nginx to handle all
requests from clients. This takes advantage of Nginx’s fast processing speed
and ability to handle large numbers of connections concurrently.

For static content, which Nginx excels at, the files will be served quickly
and directly to the client. For dynamic content, for instance PHP files, Nginx
will proxy the request to Apache, which can then process the results and
return the rendered page. Nginx can then pass the content back to the client.

This setup works well for many people because it allows Nginx to function as a
sorting machine. It will handle all requests it can and pass on the ones that
it has no native ability to serve. By cutting down on the requests the Apache
server is asked to handle, we can alleviate some of the blocking that occurs
when an Apache process or thread is occupied.

This configuration also allows you to scale out by adding additional backend
servers as necessary. Nginx can be configured to pass to a pool of servers
easily, increasing this configuration’s resilience to failure and performance.

Conclusion

As you can see, both Apache and Nginx are powerful, flexible, and capable.
Deciding which server is best for you is largely a function of evaluating your
specific requirements and testing with the patterns that you expect to see.

There are differences between these projects that have a very real impact on
the raw performance, capabilities, and the implementation time necessary to
get each solution up and running. However, these usually are the result of a
series of trade offs that should not be casually dismissed. In the end, there
is no one-size-fits-all web server, so use the solution that best aligns with
your objectives.

pdb是Python自带的类gdb、cdb的调试工具。

在Sublime Text的SublimeREPL插件提供了pdb调试应用程序的功能,但是由于手上项目package太过复杂,所以这次我就直接在Django
代码要调试的地方插入pdb代码,这样相当于直接在那个地方下了一个断点。可以在任何地方插入如下代码进行中断,即使是Django也能通过这种方式进行调试:

1
import pdb; pdb.set_trace()

pdb常用命令:

  • n:执行下一行
  • s:执行下一行,如果有函数,会进入函数体
  • c:让程序正常运行,直到遇到断点
  • p:打印某个变量
  • !:感叹号后面跟一个语句,可以直接改变某个变量
  • h:帮助
  • l:列出当前将要运行的代码块
  • b:设置断点,例如’b 12’表示在第12行下端点,’b a.py:12’表示在a.py这个文件的第12行下断点
  • cl:清楚所有的断点
  • j:跳到指定的行数
  • a:打印当前函数的参数
  • q:推出调试

Breakpoint

从3.7开始,Python新增一个内置的调试方法breakpoint(),详情见PEP553,解决了原有pdb的一些不可能完成的问题。支持开关调试,还可以利用web-pdb实现远程调试

阅读全文 »

原文地址:https://www.insp.top/learn-laravel-container

转注:本文详细介绍了依赖注入以及Laravel IoC容器的原理,深入浅出,十分容易让人理解。

**控制反转(IoC)**:由外部负责其依赖需求的行为

**依赖注入(DI)**:只要不是由内部生产,而是由外部以参数或其他形式注入的,都属于依赖注入

容器,字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦”、“依赖注入(DI)”。本文就从这里开始。

IoC 容器, laravel 的核心

Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念。

本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转) 和 DI(依赖注入),通过理解这些概念,来更加深入。更多关于 laravel 服务容器的用法建议阅读文档即可。

IoC 容器诞生的故事

讲解 IoC 容器有很多的文章,我之前也写过。但现在我打算利用当下的灵感重新来过,那么开始吧。

超人和超能力,依赖的产生!

面向对象编程,有以下几样东西无时不刻的接触:接口、类还有对象。这其中,接口是类的原型,一个类必须要遵守其实现的接口;对象则是一个类实例化后的产物,我们称其为一个实例。当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码辅助学习。

怪物横行的世界,总归需要点超级人物来摆平。_

我们把一个“超人”作为一个类,

class Superman {}

我们可以想象,一个超人诞生的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个对象,为这个对象定义一个描述他的类吧。一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们以后再丰富:

class Power {
    /**
     * 能力值
     */
    protected $ability;
    /**
     * 能力范围或距离
     */
    protected $range;

    public function __construct($ability, $range)
    {
        $this->ability = $ability;
        $this->range = $range;
    }
}

这时候我们回过头,修改一下之前的“超人”类,让一个“超人”创建的时候被赋予一个超能力:

class Superman
{
    protected $power;
 
    public function __construct()
    {
        $this->power = new Power(999, 100);
    }
}

这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。

所谓“依赖”,就是“我若依赖你,少了你就没有我”。

在一个贯彻面向对象编程的项目中,这样的依赖随处可见。少量的依赖并不会有太过直观的影响,我们随着这个例子逐渐铺开,让大家慢慢意识到,当依赖达到一个量级时,是怎样一番噩梦般的体验。当然,我也会自然而然的讲述如何解决问题。

一堆乱麻 —— 可怕的依赖

之前的例子中,超能力类实例化后是一个具体的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完全。我们现在进行修改,我们假设超人可以有以下多种超能力:

  • 飞行,属性有:飞行速度、持续飞行时间

  • 蛮力,属性有:力量值

  • 能量弹,属性有:伤害值、射击距离、同时射击个数

    我们创建了如下类:

class Flight
{
    protected $speed;
    protected $holdtime;
    public function __construct($speed, $holdtime) {}
}
 
class Force
{
    protected $force;
    public function __construct($force) {}
}
 
class Shot
{
    protected $atk;
    protected $range;
    protected $limit;
    public function __construct($atk, $range, $limit) {}
}

为了省事儿我没有详细写出construct()这个构造函数的全部,只写了需要传递的参数。

好了,这下我们的超人有点“忙”了。在超人初始化的时候,我们会根据需要来实例化其拥有的超能力吗,大致如下:

class Superman
{
    protected $power;
 
    public function __construct()
    {
        $this->power = new Fight(9, 100);
        // $this->power = new Force(45);
        // $this->power = new Shot(99, 50, 2);
        /*
        $this->power = array(
            new Force(45),
            new Shot(99, 50, 2)
        );
        */
    }
}

我们需要自己手动的在构造函数内(或者其他方法里)实例化一系列需要的类,这样并不好。可以想象,假如需求变更(不同的怪物横行地球),需要更多的有针对性的 新的超能力,或者需要 变更 超能力的方法,我们必须 重新改造超人。换句话说就是,改变超能力的同时,我还得重新制造个超人。效率太低了!新超人还没创造完成世界早已被毁灭。

这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时更换,只需要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。这样的话就不要整个重新来
过了。

对,就是这样的。

我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为“模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组”满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。

工厂模式,依赖转移!

当然,实现控制反转的方法有几种。在这之前,不如我们先了解一些好玩的东西。

我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式:
工厂模式。

工厂模式,顾名思义,就是一个类所以依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开发模式,就是 “工厂模式”。

我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅需要通过一个方法:

class SuperModuleFactory
{
    public function makeModule($moduleName, $options)
    {
        switch ($moduleName) {
            case 'Fight':   return new Fight($options[0], $options[1]);
            case 'Force':   return new Force($options[0]);
            case 'Shot':    return new Shot($options[0], $options[1], $options[2]);
        }
    }
}

这时候,超人 创建之初就可以使用这个工厂!

class Superman
{
    protected $power;
 
    public function __construct()
    {
        // 初始化工厂
        $factory = new SuperModuleFactory;
 
        // 通过工厂提供的方法制造需要的模块
        $this->power = $factory->makeModule('Fight', [9, 100]);
        // $this->power = $factory->makeModule('Force', [45]);
        // $this->power = $factory->makeModule('Shot', [99, 50, 2]);
        /*
        $this->power = array(
            $factory->makeModule('Force', [45]),
            $factory->makeModule('Shot', [99, 50, 2])
        );
        */
    }
}

可以看得出,我们不再需要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可满足需求。但这样似乎和以前区别不大,只是没有那么多 new关键字。其实我们稍微改造一下这个类,你就明白,工厂类的真正意义和价值了。

class Superman
{
    protected $power;
 
    public function __construct(array $modules)
    {
        // 初始化工厂
        $factory = new SuperModuleFactory;
 
        // 通过工厂提供的方法制造需要的模块
        foreach ($modules as $moduleName => $moduleOptions) {
            $this->power[] = $factory->makeModule($moduleName, $moduleOptions);
        }
    }
}
 
// 创建超人
$superman = new Superman([
    'Fight' => [9, 100], 
    'Shot' => [99, 50, 2]
    ]);

现在修改的结果令人满意。现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修改了或者增加了新的超能力,只需要针对修改SuperModuleFactory 即可。扩充超能力的同时不再需要重新编辑超人的类文件,使得我们变得很轻松。但是,这才刚刚开始。

再进一步!IoC 容器的重要组成 —— 依赖注入!

由 “超人” 对 “超能力” 的依赖变成 “超人” 对 “超能力模组工厂”的依赖后,对付小怪兽们变得更加得心应手。但这也正如你所看到的,依赖并未解除,只是由原来对多个外部的依赖变成了对一个 “工厂”的依赖。假如工厂出了点麻烦,问题变得就很棘手。

其实大多数情况下,工厂模式已经足够了。工厂模式的缺点就是:接口未知(即没有一个很好的契约模型,关于这个我马上会有解释)、产生对象类型单一。总之就是,还是不够灵活。虽然如此,工厂模式依旧十分优秀,并且适用于绝大多数情况。不过我们为了讲解后面的 依赖注入 ,这里就先夸大一下工厂模式的缺陷咯。

我们知道,超人依赖的模组,我们要求有统一的接口,这样才能和超人身上的注入接口对接,最终起到提升超能力的效果。

事实上,我之前说谎了,不仅仅只有一堆小怪兽,还有更多的大怪兽。嘿嘿。额,这时候似乎工厂的生产能力显得有些不足 ——由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高级的模组加入,我们必须修改工厂类(好比增加新的生产线):

class SuperModuleFactory
{
    public function makeModule($moduleName, $options)
    {
        switch ($moduleName) {
            case 'Fight':   return new Fight($options[0], $options[1]);
            case 'Force':   return new Force($options[0]);
            case 'Shot':    return new Shot($options[0], $options[1], $options[2]);
            // case 'more': .......
            // case 'and more': .......
            // case 'and more': .......
            // case 'oh no! its too many!': .......
        }
    }
}

看到没。。。噩梦般的感受!

其实灵感就差一步!你可能会想到更为灵活的办法!对,下一步就是我们今天的主要配角 —— DI (依赖注入)

由于对超能力模组的需求不断增大,我们需要集合整个世界的高智商人才,一起解决问题,不应该仅仅只有几个工厂垄断负责。不过高智商人才们都非常自负,认为自己的想法是对的,创造出的超能力模组没有统一的接口,自然而然无法被正常使用。这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口,自然就可被正常使用。

interface SuperModuleInterface
{
    /**
     * 超能力激活方法
     *
     * 任何一个超能力都得有该方法,并拥有一个参数
     *@param array $target 针对目标,可以是一个或多个,自己或他人
     */
    public function activate(array $target);
}

上文中,我们定下了一个接口 (超能力模组的规范、契约),所有被创造的模组必须遵守该规范,才能被生产。

其实,这就是 php 中 接口( interface ) 的用处和意义!很多人觉得,为什么 php 需要接口这种东西?难道不是 java 、 C#之类的语言才有的吗?这么说,只要是一个正常的面向对象编程语言(虽然 php 可以面向过程),都应该具备这一特性。因为一个 对象(object)本身是由他的模板或者原型 —— 类 (class) ,经过实例化后产生的一个具体事物,而有时候,实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,让大家编写出可以被随时替换却不会产生影响的接口。这种由编程语言本身提出的硬性规范,会增加更多优秀的特性。

虽然有些绕,但通过我们接下来的实例,大家会慢慢领会接口带来的好处。

这时候,那些提出更好的超能力模组的高智商人才,遵循这个接口,创建了下述(模组)类:

/**
 * X-超能量
 */
class XPower implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 这只是个例子。。具体自行脑补
    }
}
 
/**
 * 终极炸弹 (就这么俗)
 */
class UltraBomb implements SuperModuleInterface
{
    public function activate(array $target)
    {
        // 这只是个例子。。具体自行脑补
    }
}

同时,为了防止有些 “砖家” 自作聪明,或者一些叛徒恶意捣蛋,不遵守契约胡乱制造模组,影响超人,我们对超人初始化的方法进行改造:

class Superman
{
    protected $module;
 
    public function __construct(SuperModuleInterface $module)
    {
        $this->module = $module
    }
}

改造完毕!现在,当我们初始化 “超人” 类的时候,提供的模组实例必须是一个 SuperModuleInterface 接口的实现。否则就会提示错误。

正是由于超人的创造变得容易,一个超人也就不需要太多的超能力,我们可以创造多个超人,并分别注入需要的超能力模组即可。这样的话,虽然一个超人只有一个超能力,但超人更容易变多,我们也不怕怪兽啦!

现在有人疑惑了,你要讲的 依赖注入 呢?

其实,上面讲的内容,正是依赖注入。

什么叫做 依赖注入?

本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 。是不是豁然开朗?事实上,就是这么简单。下面就是一个典型的依赖注入:

// 超能力模组
$superModule = new XPower;
 
// 初始化一个超人,并注入一个超能力模组依赖
$superMan = new Superman($superModule);

关于依赖注入这个本文的主要配角,也就这么多需要讲的。理解了依赖注入,我们就可以继续深入问题。慢慢走近今天的主角……

更为先进的工厂 —— IoC 容器!

刚刚列了一段代码:

$superModule = new XPower;
 
$superMan = new Superman($superModule);

读者应该看出来了,手动的创建了一个超能力模组、手动的创建超人并注入了刚刚创建超能力模组。呵呵,手动。

现代社会,应该是高效率的生产,干净的车间,完美的自动化装配。

一群怪兽来了,如此低效率产出超人是不现实,我们需要自动化 —— 最多一条指令,千军万马来相见。我们需要一种高级的生产车间,我们只需要向生产车间提交一个脚本,工厂便能够通过指令自动化生产。这种更为高级的工厂,就是工厂模式的升华 —— IoC 容器。

class Container
{
    protected $binds;
 
    protected $instances;
 
    public function bind($abstract, $concrete)
    {
        if ($concrete instanceof Closure) {
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;
        }
    }
 
    public function make($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
 
        array_unshift($parameters, $this);
 
        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}

这时候,一个十分粗糙的容器就诞生了。现在的确很简陋,但不妨碍我们进一步提升他。先着眼现在,看看这个容器如何使用吧!

// 创建一个容器(后面称作超级工厂)
$container = new Container;
 
// 向该 超级工厂 添加 超人 的生产脚本
$container->bind('superman', function($container, $moduleName) {
    return new Superman($container->make($moduleName));
});
 
// 向该 超级工厂 添加 超能力模组 的生产脚本
$container->bind('xpower', function($container) {
    return new XPower;
});
 
// 同上
$container->bind('ultrabomb', function($container) {
    return new UltraBomb;
});
 
// ******************  华丽丽的分割线  **********************
// 开始启动生产
$superman_1 = $container->make('superman', ['xpower']);
$superman_2 = $container->make('superman', ['ultrabomb']);
$superman_3 = $container->make('superman', ['xpower']);
// ...随意添加

看到没?通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会执行。发现没有?我们彻底的解除了 超人与 超能力模组 的依赖关系,更重要的是,容器类也丝毫没有和他们产生任何依赖!我们通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用执行时,才会触发。

这样一种方式,使得我们更容易在创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。

实际上,真正的 IoC 容器更为高级。我们现在的例子中,还是需要手动提供超人所需要的模组参数,但真正的 IoC
容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。Laravel框架的服务容器正是这么做的。实现这种功能其实理论上并不麻烦,但我并不会在本文中写出,因为……我懒得写。

不过我告诉大家,这种自动搜寻依赖需求的功能,是通过 反射(Reflection) 实现的,恰好的,php 完美的支持反射机制!关于反射,php官方文档有详细的资料,并且中文翻译基本覆盖,足够学习和研究!

http://php.net/manual/zh/book.reflection.php

现在,到目前为止,我们已经不再惧怕怪兽们了。高智商人才集思广益,井井有条,根据接口契约创造规范的超能力模组。超人开始批量产出。最终,人人都是超人,你也可以是哦 !

回归正常世界。我们开始重新审视 laravel 的核心。

现在,我们开始慢慢解读 laravel 的核心。其实,laravel 的核心就是一个 IoC 容器,也恰好是我之前所说的高级的 IoC 容器。

可以说,laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能。很多人用到的各种功能模块比如Route(路由)、EloquentORM(数据库 ORM 组件)、Request and Response(请求和响应)等等等等,实际上都是与核心无关的类模块提供的,这些类从注册到实例化,最终被你所使用,其实都是 laravel的服务容器负责的。

我们以大家最常见的 Route 类作为例子。大家可能经常见到路由定义是这样的:

Route::get('/', function() {
    // bla bla bla...
});

实际上, Route 类被定义在这个命名空间:Illuminate\Routing\Router,文件vendor/laravel/framework/sr
c/Illuminate/Routing/Router.php。

我们通过打开发现,这个类的这一系列方法,如 get,post,any 等都不是静态(static)方法,这是怎么一回事儿?不要急,我们继续。

服务提供者

我们在前文介绍 IoC 容器的部分中,提到了,一个类需要绑定、注册至容器中,才能被“制造”。

对,一个类要被容器所能够提取,必须要先注册至这个容器。既然 laravel称这个容器叫做服务容器,那么我们需要某个服务,就得先注册、绑定这个服务到容器,那么提供服务并绑定服务至容器的东西,就是服务提供者(ServiceProvider)。

虽然,绑定一个类到容器不一定非要通过 服务提供者(ServiceProvider) 。

但是,我们知道,有时候我们的类、模块会有需要其他类和组件的情况,为了保证初始化阶段不会出现所需要的模块和组件没有注册的情况,laravel将注册和初始化行为进行拆分,注册的时候就只能注册,初始化的时候就是初始化。拆分后的产物就是现在的 服务提供者。

服务提供者主要分为两个部分,register(注册) 和 boot(引导、初始化),具体参考文档。register
负责进行向容器注册“脚本”,但要注意注册部分不要有对未知事物的依赖,如果有,就要移步至 boot 部分。

Facade

我们现在解答之前关于 Route 的方法为何能以静态方法访问的问题。实际上这个问题文档上有写,简单说来就是模拟一个类,提供一个静态魔术方法__callStatic,并将该静态方法映射到真正的方法上。

我们使用的 Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias() 函数创造的 别名
而已,这个类被定义在文件vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php。

我们打开文件一看……诶?怎么只有这么简单的一段代码呢?

<?php namespace Illuminate\Support\Facades;
 
/**
 * @see \Illuminate\Routing\Router
 */
class Route extends Facade {
 
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'router';
    }
 
}

其实仔细看,会发现这个类继承了一个叫做 Facade 的类,到这里谜底差不多要解开了。

上述简单的定义中,我们看到了 getFacadeAccessor 方法返回了一个 route,这是什么意思呢?事实上,这个值被一个
ServiceProvider 注册过,大家应该知道注册了个什么,当然是那个真正的路由类!

有人会问,Facade
是怎么实现的。我并不想说得太细,一个是我懒,另一个原因就是,自己发现一些东西更容易理解,并不容易忘记。很多细节我已经说了,建议大家自行去研究。

至此,我们已经讲的差不多了。

和平!我们该总结总结了!

无论如何,世界和平了。

这里要总结的内容就是,其实很多事情并不复杂,怕的是复杂的理论内容。我觉得很多东西一旦想通也就那么回事儿。很多人觉得 laravel这不好那不好、这里难哪里难,我只能说,laravel 的确不是一流和优秀的框架,说 laravel 是一流、优秀的框架的人,不是 laravel的粉丝那么就是跟风炒作。Laravel 最大的特点和优秀之处就是使用了很多 php 比较新(实际上并不新)的概念和技术(也就一堆语法糖)而已。因此laravel 的确符合一个适宜学习的框架。Laravel 的构思的确和其他框架有很大不同,这也要求学习他的人必须熟练 php,并 基础扎实!如果你觉得学laravel 框架十分困难,那么原因只有一个:你 php 基础不好。

另外,善于利用命名空间和面向对象的诸多特性,去追寻一些东西,你会发现,原来这一切这么容易。

一不小心,大三就在六月份的忙碌中结束了。

仿佛每年都有这么一个最忙路的时间段,而且一如往常的,依然发生在六月。这个月发生了好多事,也做了好多事,还有好多事等待我处理。还以为自己有一个月没更新博客了呢
,看看最近一篇博文,发现其实也只有20来天而已,怎么感觉好久没做自己想做的事情了呢。每天都是忙忙忙,但其实是自己不大会分配时间才导致忙得毫无头绪的。

首先,六月份结束了几门主要的课程,所以我立刻就到之前面试的公司去实习了,逃了几节课,但都没点名,有点儿略微失望,只有点名才会有心跳的感觉嘛。六月十二,一个寒
窗闲读十几年的学子第一次与别人签订了劳务合同,进入了第一次正式的实习工作,想想都有点儿小激动呢。如今,我来这个公司已经工作大概三周了,感觉还行,我想,暑假过
后我还会继续呆在这里。

第二件大事就是找房子了。这仿佛是实习生的宿命,特别是去不大的公司,没有住房补贴,没有分配的房子,再没有什么比找房子更伤脑筋更伤身体的了。其实我大可以不去租房
子,因为每天虽然上下班会花费大概三小时,但是这三小时里我也可以做其它事情嘛,但毕竟,我现在不是一个人了,以前是不会意识到有了女朋友要是没有房子会花更多的钱的
。。。反正,没有找过房子,永远不知道58同城上到底有多少虚假信息,永远不会怀念在学校1000能住一年的房子,永远不会知道在学校七八块都会被我们骂太贵太难吃的
食堂在社会上再也不会有了。天秤座遇到租房这种事,还能说什么呢,想死的心都有了。不过还好,最后找了一个远一点,但是安全舒适,还是和朋友合租,房租加上生活费基本
上和工资持平了。总之,好多好多事情我都不知道,而且以我的性格,别人跟我说我也不大会信所以,总得要自己去体会吧(其实最先已经签了一个房子,但住了一天发现太坑,
立马退了,还好当时合同签得好,没什么损失)。

再后来就是期末考试的事情了,反正没复习,那个闭卷考试的科目心里没有底,不过对一个已经挂过科还不考研的人来说,已经无所谓了,挂科了最多也就是下学期开学再来半天
嘛。

在技术方面,这个月真的没怎么学习,就偶尔看了看慕课网上的东西,在学习深度上并没有增加,我想,在公司多待一会儿应该就可以了,至少实战经验要多得多嘛。只是,现在
的公司在技术深度上可能并没有达到我想要的底部,不过也可能是我对项目了解得不够深,反正这里有高手,等我证明自己比他们强了再说。

其实,这个月最大的感悟是有些事不能忘记,阅读这个东西是绝对不能抛弃的。我现在动不动就发怒;知道自己有梦想,但是平时很少去想过到底怎么去实现,还抛弃了很多应该
有的所谓的道德方面的思想,反正就是很忙,获得像行尸走肉,没有灵魂的活着。反正就感觉自己挺俗的,我可不想这样,从初中开始我就励志要做一个平凡的人,但,绝不能平
庸。

忙,并不总是能让人充实,但阅读一定能让人充实。

安装

服务端安装

**CentOS**,需要注意的是,完全不需要搭配apache,因为SVN可以自己有一个tcp进程的,通过svnserve -d -r=/路径来启动,默认监听端口为3690

客户端安装

1
brew install svn

常用命令

服务端

1
2
3
4
5
6
7
svnadmin create 仓库名 # 新建仓库,该命令会在当前目录创建一个与仓库名同名的文件夹,文件夹下包含该库的所有信息,在`conf`目录下,passwd表示用户名和密码,格式为用户名=密码  
然后修改`svnserve.conf`,把下面几行的注释去掉:
anon-access = read
auth-access = write
password-db = passwd
authz-db = authz
realm = svnhome(注意)

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
svn status	# 查看当前目录下的改动信息
svn info # 查看当前的版本信息
svn diff # 对比当前目录下的更改
svn revert file # 放弃某个文件的更改
svn revert -R ./ # 放弃本地所有的更改

svn list URL # 列出分支和tag
svn checkout URL
svn add file # 把未跟踪的文件添加进来
svn commit file1 file2 -m "commit comment" # 直接提交文件

svn update # 更新svn仓库,相当于git pull
svn log -l 10 # 列出最近10条提交记录

post-commit hook配置

1
2
3
4
5
编辑仓库配置文件里面的hook,内容如下:
REPOS="$1"
REV="$2"s

cd /var/www/directory && /usr/bin/svn update --username user --password pass