This article mainly introduces the meaning of PHP-MSF source code and usage related problems, the need for friends follow the reference to learn it. Hope to help everyone.
Source interpretation also did a period of time, summed up his experience:
Grab the life cycle and let the code run in your head.
Analytic architecture, keyword layered boundary isolation
A good framework to understand the life cycle and architecture, the basic is already in the familiar state, followed by the fill details and coding proficiency
Here are a few more important tips:
Figure out what the tool is good at and what it's for. This information is also very easy to obtain, the documentation of the tool is usually marked out, you can use these features/features, try to point to meet
Looking at this project from an engineering point of view, primarily in line with the above architecture, in dealing with the core business, which is the function/feature above, engineering also involves security/testing/coding Specifications/language features, which are often less thought-out and less practiced when writing business code
The use of tools, recommended I now use the combination: Phpstorm + Baidu Brain Map + markdown notes + blog and the origin of PHP-MSF and other writing technology life-related blog to come and everyone eight, direct serving.
Life Cycle & Architecture
The official document produced a very good picture: Process the request flowchart. Recommend colleagues, have leisure time make similar diagram, to thinking very helpful.
Considering the life cycle & architecture based on this diagram, here's a look at some of the technical points in MSF:
Related knowledge of the association process
Extracts from technical points in MSF
Co-process
I will use my way to explain, if you need to know more, you can look at the resources I recommend later.
The class vs object is a very important set of concepts. class represents our abstraction of things, this abstract ability will be used in our future, I hope you consciously cultivate this aspect of consciousness, at least can play a role in comprehend by analogy. The object is the instantiated class, is really working, we want to discuss the process, is such a real working role.
Where does the co-process come from and where to go, and what does it do?
Think about these simple questions, perhaps your understanding of the process is even deeper, remember these keywords:
Produce. There needs to be a place to generate the co-process, you may not need to know the details, but need to know when it happened
Scheduling. There must be a lot of work together, so need to dispatch, how to dispatch it?
Destroyed. Will it be destroyed? When will it be destroyed?
Now, let's take a look at how the association is used, and notice here that I'm not comparing it with the implementation of the process, because in many cases the requirements are actually:
How to realize I don't care, I choose the best use.
MSF-Single-session Scheduling $response = yield $this->getredispool (' tw ')->get (' apicacheforabcoroutine ');//MSF-concurrent call $client1 = $this->getobject (client::class, [' http://www.baidu.com/']); yield $client 1->godnslookup (); $client 2 = $this- >getobject (Client::class, [' http://www.qq.com/']); yield $client 2->godnslookup (); $result [] = yield $client 1- >goget ('/'); $result [] = yield $client 2->goget ('/');
This is roughly the equation: using the covariance = plus yield, it's good to know where to add yield-where there is blocking IO, such as file io, network io (redis/mysql/http), and so on.
And, of course, there's a need for attention.
The scheduling sequence of the schedule, if not noticed, may degenerate into synchronous invocation.
Call chain: Yield is required on the call chain that uses yield. such as the following:
function A_test () { return yield $this->getredispool (' tw ')->get (' Apicacheforabcoroutine ');} $res = yield a_test (); If yield is not added, it becomes synchronous execution.
Compare the swoole2.0 solution:
$server = new Swoole\http\server ("127.0.0.1", 9502, Swoole_base), $server->set ([' worker_num ' = 1,]);//need to be in the co-process serv In the async callback function of ER, $server->on (' Request ', function ($request, $response) {$tcpclient = new Swoole\coroutine\client (swoole_ SOCK_TCP); You need to use the co-client $tcpclient->connect (' 127.0.0.1 ', 9501,0.5) $tcpclient->send ("Hello world\n"); $redis = new Swoole\coroutine\redis (); $redis->connect (' 127.0.0.1 ', 6379); $redis->setdefer (); Label delay Packet, implement concurrent call $redis->get (' key '); $mysql = new Swoole\coroutine\mysql (); $mysql->connect ([' Host ' = ' 127.0.0.1 ', ' user ' = ' user ', ' password ' = ' pass ', ' database ' = ' Test ',]); $mysql->setdefer (); $mysql->query (' Select Sleep (1) '); $httpclient = new Swoole\coroutine\http\client (' 0.0.0.0 ', 9599); $httpclient->setheaders ([' Host ' = ' api.mp.qq.com ']); $httpclient->set ([' Timeout ' = 1]); $httpclient->setdefer (); $httpclient->get ('/'); $tcp _res = $tcpclient->recv (); $Redis_res = $redis->recv (); $mysql _res = $mysql->recv (); $http _res = $httpclient->recv (); $response->end (' Test end ');}); $server->start ();
The benefits of using the swoole2.0 solution are obvious:
You don't have to add yield.
Concurrent calls do not have to pay attention to the order of yield, use defer () delay to receive packets
However, there is no way to directly use the association = Plus yield such a simple equation, the above example needs to be used in conjunction with the Swoole server + Swoole co-client:
Server generates a co-process when an asynchronous callback is triggered
Client-triggered co-scheduling
Destroying the coprocessor at the end of an asynchronous callback execution
This leads to 2 questions:
What to do in asynchronous callbacks that are not in the Swoole coprocessor: Use Swoole\coroutine::create () to explicitly generate a co-process
What to do if you need to use a different co-Client: This is the goal of Swoole3, Swoole2.0 can consider using a co-task to disguise
So it looks like it's a little easier to use the co-process = plus yield? I do not think so, to add some points of view, everyone's own discretion:
Using yield, based on PHP generator + self Implementation of the PHP co-scheduler, want to use it without error, such as the above scheduling sequence, you still need to figure out the implementation of this piece
Swoole2.0 's native way, it's easier to understand, just know the timing of the process generation/dispatch/destruction can be used well
Swoole2.0 is it a very lossy performance to create and destroy the co-processes frequently in asynchronous callbacks? --No, it's actually some memory operation, much smaller than the process/object
Extracts from technical points in MSF
MSF has a lot to do with design, and a lot of code is worth drawing on.
Request context
This is a very important concept from FPM to swoole HTTP server. FPM is a multi-process pattern, although variables such as $_post are called hyper-global variables, but these variables are isolated between different fpm processes. But in Swoole HTTP server, a worker process processes multiple requests asynchronously, and a simple understanding is the following equation:
FPM worker:http request = 1:1swoole Worker:http request = 1:n
So, we need a new way to isolate the request.
In the programming language, there is a professional vocabulary scope (scope). The scope/life cycle is usually used, so the concept of life cycle that I have been emphasizing is really important.
The swoole itself is isolated:
$http = new Swoole_http_server ("127.0.0.1", 9501), $http->on (' request ', function ($request, $response) { $ Response->end ("
MSF also has a layer of encapsulation on the context that allows the context to look as it pleases:
You can almost use this method to complete any required logic $this->getcontext ()->xxxmodule->xxxmodulefunction ();
Details to view the src/helpers/context.php file
Object Pool
The concept of object pooling may be unfamiliar, with the aim of reducing the frequent creation and destruction of objects in order to improve performance, MSF is well encapsulated and easy to use:
GetObject () can be/** @var demomodel $demoModel */$demoModel = $this->getobject (Demomodel::class, [1, 2]);
The specific code for the object pool is under src/base/pool.php:
The underlying uses reflection to implement the object's dynamic creation
Public function Get ($class, ... $args) { $poolName = Trim ($class, ' \ \ '); if (! $poolName) { return null; } $pool = $this->map[$poolName]?? null; if ($pool = = null) { $pool = $this->applynewpool ($poolName); } if ($pool->count ()) { $obj = $pool->shift (); $obj->__isconstruct = false; return $obj; } else { //Use reflection $reflector = new \reflectionclass ($poolName); $obj = $reflector->newinstancewithoutconstructor (); $obj->__usecount = 0; $obj->__gentime = time (); $obj->__isconstruct = false; $obj->__dslevel = Macro::D s_public; Unset ($reflector); return $obj; }}
Using Splstack to manage objects
Private Function Applynewpool ($poolName) { if (array_key_exists ($poolName, $this->map)) { throw new Exception (' The name is exists in Pool map '); } $this->map[$poolName] = new \splstack (); return $this->map[$poolName];} Management Object $pool->push ($classInstance); $obj = $pool->shift ();
Connection Pooling & Proxy
Connection Pool Pools
The concept of connection pooling is not to be discussed, so let's look directly at the implementation in MSF, where the code is src/pools/asynpool.php:
Public function __construct ($config) { $this->callbacks = []; $this->commands = new \splqueue (); $this->pool = new \splqueue (); $this->config = $config;}
Here you use the Splqueue to manage connections and commands that need to be executed. Can compare with above, think about why one uses Splstack, one uses Splqueue.
Agent Proxy
Proxies are further encapsulated on the basis of connection pooling, and MSF provides 2 packages:
Master slave
Cluster cluster
View the code in the example App\controllers\redis:
Class Redis extends controller{ //Redis connection pool Read and write sample public function Actionpoolsetget () { yield $this Getredispool (' P1 ')->set (' Key1 ', ' val1 '); $val = yield $this->getredispool (' P1 ')->get (' Key1 '); $this->outputjson ($val); } The Redis agent uses the sample (distributed) public function Actionproxysetget () {for ($i = 0; $i <=, $i + +) { yield $this ->getredisproxy (' cluster ')->set (' Proxy '. $i, $i); } $val = yield $this->getredisproxy (' cluster ')->get (' Proxy22 '); $this->outputjson ($val); } The Redis agent uses the example (master-slave) public function Actionmaserslavesetget () {for ($i = 0; $i <=; $i + +) { Yiel D $this->getredisproxy (' Master_slave ')->set (' M '. $i, $i); } $val = yield $this->getredisproxy (' Master_slave ')->get (' M66 '); $this->outputjson ($val); }}
The agent is the connection pool on the basis of further things. Take master-slave mode as an example:
Master-Slave Policy: Read the main library, write from the library
Agent to do things:
To decide whether to read or write, select the appropriate library to perform the operation
Public Library
MSF's approach to public libraries, in the hope that different functional components can be pluggable, can be seen in both the Laravel framework and the Symfony framework, which are made up of the framework core plus a package. This kind of thought I was very recommended, but carefully look at Baidu brain Map-php-msf Source interpretation of this map, you will find the class and the dependency between the class, layered/boundary does not do well. If you have read my previous Blog-laravel source interpretation/blog-yii source Interpretation, contrast will feel very obvious.
However, this does not mean that the code is not good, at least the functional code, almost all good code. The superiority created from outside the function is more of a longing for a better life-and a little better.
Aop
PHP AOP Extension: HTTP://PECL.PHP.NET/PACKAGE/AOP
PHP-AOP Extension Introduction | Rango:http://rango.swoole.com/archives/83
AOP, aspect-oriented programming, Han boss's Blog-php-aop extension Introduction | Rango can see.
Need not understand a new thing, first see what this thing has the function:
AOP, separating business code from business-independent code, scenarios such as logging/performance statistics/security control/Transaction processing/exception handling/caching, etc.
Here is a quote from the code in the public number of the programmer, DD-Suphing, to let everyone feel the following:
Also CRUD, not using AOP
@postmapping ("/delete") public map<string, object> Delete (long id, String lang) {map<string, object> data = NE W hashmap<string, object> (); Boolean result = false; try { //language (Chinese and English hints differ) Locale local = "zh". Equalsignorecase (lang)? Locale.CHINESE:Locale.ENGLISH; result = Configservice.delete (ID, local); Data.put ("code", 0); } catch (Checkexception e) { //parameter checksum error, such exceptions are known exceptions, do not need to print the stack, the return code is-1 data.put ("code", 1); Data.put ("msg", E.getmessage ()); } catch (Exception e) { ///Other unknown exception, need to print stack analysis, return code is log.error (e); Data.put ("code"); Data.put ("msg", e.tostring ()); } data.put ("result", result); return data;}
Using AOP
@postmapping ("/delete") public resultbean<boolean> Delete (long id) {return new resultbean<boolean> ( Configservice.delete (ID));}
The code only uses one line, the required characteristics of a few, do you want to write such CRUD code?
Configuration file Management
Define the pain points for configuration management first:
Whether to support thermal updates, resident memory needs to be considered
Consider different environments: Dev Test production
Easy to use
Hot more actually can be regarded as the overall needs of the resident memory server, the current PHP common solution is inotify, can refer to my previous Blog-swoft source interpretation.
MSF uses third-party libraries to parse the processing configuration file, which focuses on a array_merge () detail:
$a = [' a ' = = [ ' A1 ' + ' A1 ',]]; $b = [' a ' = = [' B1 ' + ' B1 ',]]; $arr = Array_merge ($a, $b);//Note, array _merge () does not loop merge Var_dump ($arr);//result Array (1) {["A"]=> Array (1) { ["B1"]=> string (2) "B1"}}
Configuration used in MSF:
$ids = $this->getconfig ()->get (' Params.mock_ids ', []);//Compare Laravel$ids = Cofnig (' Params.mock_ids ', []);
It seems to be simpler in laravel, in fact, the function is loaded by composer AutoLoad, which wraps a layer on the actual operation. If you want to do this, look for your own needs.
Written in the last
The most complex part of MSF is in the service startup phase, and the inheritance is long:
AppServer, Msfserver, Httpserver, Server---
Another difficult point is the mongodbtask implementation principle.
MSF also encapsulates a number of useful features, RPC/Message Queuing/restful, which you can explore with your own documentation.