Contacts

Greetings to you, dear friend! Affiliate Program Poor Success PHP

Standard storage mechanism of user sessions in PHP - storage in files. However, when the application is running on multiple servers for balancing the load, it is necessary to store these sessions in the repository accessible to each application server. In this case, it is good for storing sessions Redis.

The most popular solution is the expansion of phpredis. It is enough to install the extension and configure php.ini and the sessions will be automatically saved in Redis without changing the application code.

However, such a solution has a drawback - the lack of blocking the session.

When using a standard session storage mechanism in files, an open session blocks the file until it is closed. With several simultaneous appeals, access to the session, new requests will be expected until the previous one will complete work with the session. However, when using a phpredis there is no blocking mechanism. With several asynchronous requests, a race is at the same time, and some data recorded in the session may be lost.

It is easy to check. We send 100 requests to the server asynchronously, each of which writes its parameter to the session, then consider the number of parameters in the session.

Test script

"; Break;)


As a result, we obtain that in the session is not 100 parameters, and 60-80. The remaining data we lost.
In real applications, of course, 100 simultaneous requests will not be, however, the practice shows that even with two asynchronous simultaneous requests, the data recorded by one of the requests is quite often maintained by another. Thus, the use of phpredis extension for storing sessions is unsafe and can lead to data loss.

As one of the solutions of the problem - your SessionHandlerSupporting locks.

Sales

To set the session lock, set the value of the lock key to randomly generated (based on UniQID) value. The value must be unique so that any parallel request could access.

Protected Function LockSession ($ SessionID) ($ Attempts \u003d (1000000 * $ This-\u003e Lockmaxwait) / $ this-\u003e spinlockwait; $ this-\u003e token \u003d uniqid (); $ this-\u003e< $attempts; ++$i) { $success = $this->redis-\u003e set ($ this-\u003e getrediskey ($ this-\u003e lockkey), $ this-\u003e token, ["nx",]); if ($ succession) ($ this-\u003e locked \u003d true; Return True;) USLEEP ($ this-\u003e spinlockwait); ) Return False; )
The value is installed with the flag NX., That is, the installation occurs only if there is no such key. If such a key exists, we make a re-attempt after a while.

You can also use a limited life time in the radish, however, the script operation time can be changed after setting the key, and the parallel process can access the session until it is completed with it in the current script. When the script is completed, the key is in any case deleted.

When unlocking the session when the script is completed to delete the key, use Lua.-scenario:

Private Function UnlockSession () ($ script \u003d<<redis-\u003e Eval ($ script, array ($ this-\u003e getrediskey ($ this-\u003e lockkey), $ this-\u003e token), 1); $ this-\u003e locked \u003d false; $ this-\u003e token \u003d null; )
Use the command Del. It is impossible, since using it you can delete the key installed by another script. Such a scenario guarantees deletion only if the blocking key corresponds to a unique value set by the current script.

Full class code

class RedisSessionHandler implements \\ SessionHandlerInterface (protected $ redis; protected $ ttl; protected $ prefix; protected $ locked; private $ lockKey; private $ token; private $ spinLockWait; private $ lockMaxWait; public function __construct (\\ Redis $ redis, $ prefix \u003d "Phpredis_session:", $ spinlockwait \u003d 200000) ($ this-\u003e redis \u003d $ redis; $ this-\u003e TTL \u003d INI_GET ("GC_MAXLIFETIME"); $ inimaxExecutionTime \u003d INI_GET ("max_execution_time"); $ this-\u003e lockmaxwait \u003d $ INIMAXEXECTIONTIME? $ inimaxExecutionTime * 0.7: 20; $ this-\u003e prefix \u003d $ prefix; $ this-\u003e locked \u003d false; $ this-\u003e lockkey \u003d null; $ this-\u003e spinlockwait \u003d $ spinlockwait;) Public Function Open ($ Savepath , $ SessionName) (Return True;) Protected Function LockSession ($ Sessionid) ($ Attempts \u003d (1000000 * $ This-\u003e Lockmaxwait) / $ this-\u003e spinlockwait; $ this-\u003e token \u003d uniqid (); $ this-\u003e lockkey \u003d $ sessionid. ".lock"; for ($ i \u003d 0; $ i< $attempts; ++$i) { $success = $this->redis-\u003e set ($ this-\u003e getrediskey ($ this-\u003e lockkey), $ this-\u003e token, ["nx",]); if ($ succession) ($ this-\u003e locked \u003d true; Return True;) USLEEP ($ this-\u003e spinlockwait); ) Return False; ) Private Function UnlockSession () ($ script \u003d<<redis-\u003e Eval ($ script, array ($ this-\u003e getrediskey ($ this-\u003e lockkey), $ this-\u003e token), 1); $ this-\u003e locked \u003d false; $ this-\u003e token \u003d null; ) Public Function Close () (if ($ this-\u003e locked) ($ this-\u003e unlocksession ();) Return True;) Public Function Read ($ SessionID) (if (! $ this-\u003e Locked) (if (! $ this-\u003e LockSession ($ sessionid)) (Return false;)) Return $ this-\u003e Redis-\u003e GetReDiskey ($ sessionid))?: "";) Public Function Write ($ sessionid, $ DATA) (if ($ this-\u003e TTL\u003e 0) ($ this-\u003e Redis-\u003e SETEX ($ this-\u003e getrediskey ($ sessionid), $ this-\u003e TTL, $ DATA);) ELSE ($ this-\u003e Redis-\u003e set ($ this-\u003e getrediskey ($ sessionid), $ data);) Return True;) Public Function Destroy ($ SessionID) ($ this-\u003e Redis-\u003e Del ($ this-\u003e getrediskey ($ sessionid) ); $ this-\u003e close (); Return True;) Public Function GC ($ Lifetime) (Return True) Public Function Setttl ($ TTL) ($ this-\u003e TTL \u003d $ TTL;) Public Function GetLockMaxWait () ( Return $ this-\u003e lockmaxwait;) Public Function SetLockmaxwait ($ Lockmaxwait) ($ this-\u003e lockmaxwait \u003d $ lockmaxwait;) Protected Function Getrediskey ($ KEY) (EMPTY ($ this-\u003e prefix)) (Return $ Key; ) Return. $ this-\u003e prefix. $ Key; ) Public Function __Destruct () ($ this-\u003e close ();))

Connection

$ redis \u003d new redis (); If ($ Redis-\u003e Connect ("11.111.111.11", 6379) && $ Redis-\u003e SELECT (0)) ($ Handler \u003d New \\ Suffi \\ RedisessionHandler \\ RedisessionHandler ($ Redis); session_set_save_handler ($ handler);) session_start ();

Result

After connecting ours SessionHandler Our test script confidently shows 100 parameters in the session. At the same time, despite blocking, the total processing time of 100 requests has grown slightly. In real practice, there will be no such number of simultaneous requests. However, the script operation is usually more substantial, and with simultaneous queries there may be noticeable waiting. Therefore, you need to think about reducing the time of working with the script session (call session_start () only if necessary work with the session and session_write_close () Upon completion of working with it)

Requests the server without rebooting the page. This is a low-level method that has a large number of settings. It underlies the work of all other AJAX methods. It has two uses:

uRL - Add request.
settings - In this parameter, you can set the settings for this request. Sets the object in the format (name: value, name: value ...). None of the settings is mandatory. You can set the default settings using the $ .ajaxsetUp () method.

List of settings

↓ Title: Type (default value)

When executing a query, the headers (Header) indicate the allowable types of content expected from the server. The values \u200b\u200bof these types will be taken from the ACCEPTS parameter.

By default, all requests without rebooting the page occur asynchronously (that is, after sending a request to the server, the page does not stop its work while waiting for an answer). If you need a synchronous execution of the query, set the parameter to FALSE. Crossdomain requests and requests like "JSONP" cannot be performed in synchronous mode.

Keep in mind that the execution of queries in synchronous mode may result in blocking the page until the request is fully fulfilled.

This field contains a function that will be called directly before sending a AJAX request to the server. This function can be useful for modifying the JQXhr object (in early versions Libraries (up to 1.5), instead of JQXHR uses XmlHttprequest). For example, you can change / specify the necessary headers (headers) i.d. The JQXHR object will be transmitted to the first argument function. The second argument passs the query settings.

In this field, you can specify additional query headers (Header). These changes will be entered before the beforesend call, in which the final editors may be made.

When transferring this setting to True, the query will be executed with the status "Successful", only if the response from the server differs from the previous answer. JQuery checks this fact referring to the Last-modified header. Starting with JQuery-1.4, in addition to the Last-Modified and "ETAG" is also checked (both are provided by the server and are necessary to notify the browser that the requested data from the server is not changed from the previous request).

Allows you to set the status of the location page source (as if it happened via File protocol), even if jQuery recognized it otherwise. The library decides that the page is running locally in the case of the following protocols: File, * -Extension, and Widget.

It is recommended to set the value of the parameter islocal Globally - using the $.ajaxsetUp () function, and not in the settings of individual AJAX requests.

Specifies the name of the parameter that is added to the URL with a JSONP query (by default, "Callback" is used - "http: //sitename.ru? Callback \u003d ...").

Starting with jQuery-1.5, specifying in this False parameter, you will prevent the addition to the URL additional parameter. In this case, it is necessary to explicitly set the value of the JSONPCallback property. For example, this is: (JSONP: false, JSONPCallback: "CallbackName").

Defines the name of the function that will be called when answering the server on the JSONP request. By default, JQuery generates an arbitrary name of this feature, which is a more preferred option that simplifies the operation of the library. One of the reasons in which it is to specify your own JSONP query processing function is to improve the caching of GET requests.

Starting with jQuery-1.5, you can specify a function in this parameter in order to process server response yourself. In this case, the specified function must return the data received from the server (in the specified function they will be available in the first parameter).

By default, all the data transmitted to the server is pre-converted to the string (URL format: FName1 \u003d Value1 & FName2 \u003d Value2 & ...) the corresponding "Application / X-WWW-FORM-URLENCODED". If you need to send data that cannot be subjected to similar processing (for example, DOM document), then you should disable the ProcessData option.

This parameter is used for cross-domain Ajax types of type GET, Datatype can be or "jsonp", or "script". Specifies the encoding in which a crossdomain query will be performed. This is necessary, if the server on someone else's domain uses an encoding other than encoding on a native domain server.

(This setting appeared in jQuery-1.5) A set of steam, in which the query execution codes are compared to the functions that will be caused. For example, for code 404 (do not exist), you can draw a message to the screen:

$.ajax (Statuscode: (404: Function () (Alert ( "Page not found") ; } } } ) ;

Functions that respond to a successful query codes will receive the same arguments as the functions of a successful execution of the query (specified in the Success parameter), and the functions that work on the error codes will be the same as the Error functions.

The function that will be called in the case of a successful completion of the server request. It will be transmitted three parameters: data sent by the server and have already passed the preliminary processing (which is excellent for different datatype). The second parameter is a string with execution status. The third parameter contains the JQXHR object (in earlier versions of the library (up to 1.5), instead of JQXHR uses xmlhttprequest). Starting with jQuery-1.5, instead of a single function, this parameter can take an array of functions.

Time for having a response from server. Set in milliseconds. If this time is exceeded, the query will be completed with an error and the ERROR event will occur (see the description above), which will have the status "timeout".

The time is counting from the moment of calling the $ .ajax function. It may happen that at this moment several other requests will be launched and the browser will postpone the execution of the current request. In this case timeout. It may end, although in fact, the request has not yet been launched.

In jQuery-1.4 and younger, at the end of the waiting time, the XmlHttprequest object will switch to the error state and access to its fields can cause an exception. IN Firefox 3.0+ Queries of the Script and JSONP type will not be interrupted when the waiting time is exceeded. They will be completed even after this time will expire.

A function that will provide an XMLHttpRequest object. By default, the IE browsers this object is ActiveXObject, and in other cases it is xmlhttprequest. With this parameter, you can implement your own version of this object.

(This setting appeared in jQuery-1.5.1) A set of pairs (name: Sign) To change / add the values \u200b\u200bof the corresponding fields of the XMLHTTPRequest object. For example, you can set its withcredentials property in True, when performing a crossdomain query:

$ .ajax ((URL: A_CROSS_DOMAIN_URL, XHRFIELDS: (WITHCREDENTIALS: TRUE)));

IN jQuery-1.5 The withcredentials property is not supported by the native XmlHttprequest and with a crossdomen query this field will be ignored. In all the following versions of the library, it is fixed.

Event handlers

Beforesert, Error, DataFilter, Success and Complete settings (their description in the previous section) allow you to establish event handlers that occur at certain points in the execution of each AJAX query.

beforesend. It happens immediately before sending a request to the server. error happens in case of unsuccessful execution of the query. dataFilter. happens at the time of arriving data from the server. Allows you to handle "raw" data sent by the server. success. It occurs in the case of a successful completion of the request. complete. happens in case of any completion of the request.

Example simple use. We will withdraw the message with a successful query execution:

$ .ajax ((URL: "AJAX / TEST.HTML", SUCCESS: FUNCTION () (Alert ("Load Was Performed.");)));

Starting with jQuery-1.5, the $.ajax () method returns the JQXHR object, which, among other things, implements the Deferred interface, which allows you to set additional execution handlers. In addition to the standard .done (), .fail () i.then () methods for Deferred (), with which you can install handlers, are implemented in JQXhr.Success (), .error () and.comPlete (). This is done to meet the usual names of the methods with which the handlers of the execution of AJAX queries are set. However, starting with JQuery-1.8, these three methods will be unwanted for use.

For some types of queries, such as JSONP or crossdomains GET queries, it is not provided for using XMLHttpRequest objects. In this case, transmitted to XMLHttpRequest and TextStatus handlers will contain undefined value.

Inside handlers, this variable will contain the parameter value context.. In case it has not been specified, this will contain an object of settings.

DataType

The $.ajax () function will find out about the type of data server sent from the server itself (MIME tools). In addition, it is possible to personally specify (clarify) how to interpret this data. This is done using the Datatype parameter. Possible values \u200b\u200bof this parameter:

"XML" - The resulting XML document will be available in text form. You can work with him standard means jQuery (as well as with HTML document). "HTML" - The HTML received will be available in text form. If it contains scripts in tags



In this example using jQuery method .load () We are pressed on the element

Did you like the article? Share it