Home  >  Article  >  php教程  >  关于PHP的Session处理的问题

关于PHP的Session处理的问题

WBOY
WBOYOriginal
2016-06-06 19:52:221291browse

问题源自于深空博客的这篇文章《由会话重定向看到的对象销毁问题》,嗯,我以为这种问题早有人处理过了,因为2年前我就解决了此问题。解决办法已经发在phpchina.com的原创区:《关于PHP的Session处理的问题》。不过我在自己的博客上也发表同样的一篇帖子,留

问题源自于深空博客的这篇文章《由会话重定向看到的对象销毁问题》,嗯,我以为这种问题早有人处理过了,因为2年前我就解决了此问题。解决办法已经发在phpchina.com的原创区:《关于PHP的Session处理的问题》。不过我在自己的博客上也发表同样的一篇帖子,留作备份。

 

在专家板块看到有人提出对Session处理机制的问题,原文《由会话重定向看到的对象销毁问题》。由于本人没有在专家板块发帖的资格,所以在这里发。

大概在08年年头我开始放弃Ruby on Rails转移到PHP开发,并以RoR的一些精神开发基于PHP的MVC框架,08年年底的时候,曾在phpchina这里发过一帖《自写MVC框架 Agi PHPMVC(核心)》,可以这么说,从我接触PHP以来一直是以自写的MVC框架在进行开发。目前该框架取名Agi on Rails,已经进入正式版的1.2版,下一个release版本将会考虑开源。该框架已经成功稳定的运行在多个Server Env(Windows、Linux,IIS、Apache、Lighttpd、Nginx),开发过超过20个项目,承受过一天超过1200万PV的洗礼(预计并发峰值在200左右)。为何加这一个插曲,是为了强调,我是坚持将数据操作写在Model层的,而Session处理的逻辑,是被设计成一个 Model,而随着众多Model被Controler和View层调用。而开发者,是可以针对Session这个模块进行后期的高级的逻辑封装的。

废话就不多说了,解决方案如下: 

 

<p><span> 1</span> <span>//</span><span> 数据库连接的抽象层</span><span><br></span><span> 2</span> <span> </span><span>abstract</span><span>class</span><span> DB_Connector {<br></span><span> 3</span> <span>    <br></span><span> 4</span> <span>protected</span><span>static</span><span><br></span><span> 5</span> <span>$_register</span><span>=</span><span>array</span><span>();<br></span><span> 6</span> <span><br></span><span> 7</span> <span>static</span><span>public</span><span>function</span><span> connect(</span><span>$anyKey</span><span>) {<br></span><span> 8</span> <span>//</span><span> 假设传入的$anyKey指定要使用MySQL进行连接<br></span><span> 9</span> <span>        // 这中间的一些判断这里就忽略了</span><span><br></span><span>10</span> <span> </span><span>if</span><span> (</span><span>!</span><span>isset</span><span>(self</span><span>::</span><span>$_register</span><span>[</span><span>$anyKey</span><span>])) {<br></span><span>11</span> <span>            self</span><span>::</span><span>$_register</span><span>[</span><span>$anyKey</span><span>] </span><span>=</span><span>new</span><span> DB_Connector_MySQL();<br></span><span>12</span> <span>        }<br></span><span>13</span> <span>return</span><span> self</span><span>::</span><span>$_register</span><span>[</span><span>$anyKey</span><span>];<br></span><span>14</span> <span>    }<br></span><span>15</span> <span><br></span><span>16</span> <span>static</span><span>public</span><span>function</span><span> disconnect(</span><span>$anyKey</span><span>) {<br></span><span>17</span> <span>        self</span><span>::</span><span>connect(</span><span>$anyKey</span><span>)</span><span>-></span><span>disconnect();<br></span><span>18</span> <span>    }<br></span><span>19</span> <span><br></span><span>20</span> <span>static</span><span>public</span><span>function</span><span> handleDisconnect(</span><span>$anyKey</span><span>) {<br></span><span>21</span> <span>        self</span><span>::</span><span>connect(</span><span>$anyKey</span><span>)</span><span>-></span><span>handleDisconnect();<br></span><span>22</span> <span>    }<br></span><span>23</span> <span>}<br></span><span>24</span> <span><br></span><span>25</span> <span> </span><span>//</span><span> 数据库连接的驱动层</span><span><br></span><span>26</span> <span> </span><span>class</span><span> DB_Connector_MySQL {<br></span><span>27</span> <span>    <br></span><span>28</span> <span>protected</span><span><br></span><span>29</span> <span>$_connector</span><span>=</span><span>null</span><span>,</span><span><br></span><span>30</span> <span>$_isHandleDisconnect</span><span>=</span><span>false</span><span>;<br></span><span>31</span> <span><br></span><span>32</span> <span>public</span><span>function</span><span> __construct() {<br></span><span>33</span> <span>//</span><span> 执行具体的连接</span><span><br></span><span>34</span> <span> </span><span>$this</span><span>-></span><span>_connector </span><span>=</span><span>new</span><span> MySQLDriver();<br></span><span>35</span> <span>    }<br></span><span>36</span> <span>    <br></span><span>37</span> <span>public</span><span>function</span><span> __destruct() {<br></span><span>38</span> <span>if</span><span> (</span><span>!</span><span>$this</span><span>-></span><span>_isHandleDisconnect)<br></span><span>39</span> <span>$this</span><span>-></span><span>disconnect();<br></span><span>40</span> <span>    }<br></span><span>41</span> <span><br></span><span>42</span> <span>public</span><span>function</span><span> disconnect() {<br></span><span>43</span> <span>$this</span><span>-></span><span>_connector </span><span>=</span><span>null</span><span>;<br></span><span>44</span> <span>    }<br></span><span>45</span> <span><br></span><span>46</span> <span>public</span><span>function</span><span> handleDisconnect() {<br></span><span>47</span> <span>$this</span><span>-></span><span>_isHandleDisconnect </span><span>=</span><span>true</span><span>;<br></span><span>48</span> <span>    }<br></span><span>49</span> <span>}<br></span><span>50</span> <span><br></span><span>51</span> <span> </span><span>//</span><span> Session的实现层<br></span><span>52</span> <span>// Any_ActiveRecord是Model的抽象层,这里就不实现了</span><span><br></span><span>53</span> <span> </span><span>class</span><span> Session </span><span>extends</span><span> Any_ActiveRecord {<br></span><span>54</span> <span>    <br></span><span>55</span> <span>protected</span><span>static</span><span><br></span><span>56</span> <span>$_connectorKey</span><span>=</span><span>'</span><span>Any</span><span>'</span><span>;<br></span><span>57</span> <span><br></span><span>58</span> <span>//</span><span> 标准实现</span><span><br></span><span>59</span> <span>static</span><span>public</span><span>function</span><span> open() {<br></span><span>60</span> <span>//</span><span> 一旦将Session处理转移给DB层面去控制<br></span><span>61</span> <span>        // 就意味着数据库连接的释放,也必须转交给这个Session模块来处理</span><span><br></span><span>62</span> <span>        DB_Connector</span><span>::</span><span>handleDisconnect(self</span><span>::</span><span>$_connectorKey</span><span>);<br></span><span>63</span> <span>//</span><span> 其他启动配置,包括Session GC清理的基数等等</span><span><br></span><span>64</span> <span>    }<br></span><span>65</span> <span>    <br></span><span>66</span> <span>//</span><span> 标准实现<br></span><span>67</span> <span>    // </span><span><br></span><span>68</span> <span>static</span><span>public</span><span>function</span><span> pick(</span><span>$sId</span><span>) {<br></span><span>69</span> <span>        <br></span><span>70</span> <span>    }<br></span><span>71</span> <span><br></span><span>72</span> <span>//</span><span> 标准实现</span><span><br></span><span>73</span> <span>static</span><span>public</span><span>function</span><span> dump(</span><span>$sId</span><span>,</span><span>$val</span><span>) {<br></span><span>74</span> <span>        <br></span><span>75</span> <span>    }<br></span><span>76</span> <span><br></span><span>77</span> <span>//</span><span> 标准实现</span><span><br></span><span>78</span> <span>static</span><span>public</span><span>function</span><span> destroy(</span><span>$sId</span><span>) {<br></span><span>79</span> <span>        <br></span><span>80</span> <span>    }<br></span><span>81</span> <span><br></span><span>82</span> <span>//</span><span> 标准实现</span><span><br></span><span>83</span> <span>static</span><span>public</span><span>function</span><span> gc() {<br></span><span>84</span> <span>        <br></span><span>85</span> <span>    }<br></span><span>86</span> <span><br></span><span>87</span> <span>//</span><span> 标准实现</span><span><br></span><span>88</span> <span>static</span><span>public</span><span>function</span><span> close() {<br></span><span>89</span> <span>//</span><span> 一切OK,再由Session Close的时候,释放数据库连接</span><span><br></span><span>90</span> <span>        DB_Connector</span><span>::</span><span>disconnect(self</span><span>::</span><span>$_connectorKey</span><span>);<br></span><span>91</span> <span>    }<br></span><span>92</span> <span>}</span></p>

至此,第一个问题解决了,就是关于数据库连接的释放问题。但是这里存在第二个问题(假如你在使用的框架,取出的Session是一个数组,或者你直接就取出的是一个数组,可以忽略第二个问题),就是按照常理,一个Session经由Model取出,理应被是一个Session的实例,然后,由于PHP本身的运行机制的问题,变量的释放,往往早于Session的注销。这时就要发挥出OO的本色了:

 

根据上述的Session类,我们进行一点点改造:

 

<p><span> 1</span> <span>//</span><span> Session的实现层<br></span><span> 2</span> <span>// Any_ActiveRecord是Model的抽象层,这里就不实现了</span><span><br></span><span> 3</span> <span>class</span><span> Session </span><span>extends</span><span> Any_ActiveRecord {<br></span><span> 4</span> <span>    <br></span><span> 5</span> <span>protected</span><span>static</span><span><br></span><span> 6</span> <span>$_connectorKey</span><span>=</span><span>'</span><span>Any</span><span>'</span><span>,</span><span><br></span><span> 7</span> <span>$_currSess</span><span>=</span><span>null</span><span>;<br></span><span> 8</span> <span><br></span><span> 9</span> <span>//</span><span> 标准实现</span><span><br></span><span>10</span> <span>static</span><span>public</span><span>function</span><span> open() {<br></span><span>11</span> <span>//</span><span> 一旦将Session处理转移给DB层面去控制<br></span><span>12</span> <span>        // 就意味着数据库连接的释放,也必须转交给这个Session模块来处理</span><span><br></span><span>13</span> <span>        DB_Connector</span><span>::</span><span>handleDisconnect(self</span><span>::</span><span>$_connectorKey</span><span>);<br></span><span>14</span> <span>//</span><span> 其他启动配置,包括Session GC清理的基数等等</span><span><br></span><span>15</span> <span>    }<br></span><span>16</span> <span>    <br></span><span>17</span> <span>//</span><span> 标准实现<br></span><span>18</span> <span>    // 拿出Session</span><span><br></span><span>19</span> <span>static</span><span>public</span><span>function</span><span> pick(</span><span>$sId</span><span>) {<br></span><span>20</span> <span>        self</span><span>::</span><span>$_currSess</span><span>=</span><span> self</span><span>::</span><span>find_by_sess_id(</span><span>$sId</span><span>);<br></span><span>21</span> <span>if</span><span> (</span><span>!</span><span>self</span><span>::</span><span>$_currSess</span><span>-></span><span>isEmpty())<br></span><span>22</span> <span>return</span><span> self</span><span>::</span><span>$_currSess</span><span>-></span><span>value;<br></span><span>23</span> <span>return</span><span>false</span><span>;<br></span><span>24</span> <span>    }<br></span><span>25</span> <span><br></span><span>26</span> <span>//</span><span> 标准实现</span><span><br></span><span>27</span> <span>static</span><span>public</span><span>function</span><span> dump(</span><span>$sId</span><span>,</span><span>$val</span><span>) {<br></span><span>28</span> <span>//</span><span> 新访客</span><span><br></span><span>29</span> <span>if</span><span> (self</span><span>::</span><span>$_currSess</span><span>-></span><span>isEmpty())<br></span><span>30</span> <span>            self</span><span>::</span><span>$_currSess</span><span>-></span><span>sess_id </span><span>==</span><span>$sId</span><span>;<br></span><span>31</span> <span>        self</span><span>::</span><span>$_currSess</span><span>-></span><span>value </span><span>=</span><span>$val</span><span>;<br></span><span>32</span> <span>        self</span><span>::</span><span>$_currSess</span><span>-></span><span>save();<br></span><span>33</span> <span>    }<br></span><span>34</span> <span><br></span><span>35</span> <span>//</span><span> 标准实现</span><span><br></span><span>36</span> <span>static</span><span>public</span><span>function</span><span> destroy(</span><span>$sId</span><span>) {<br></span><span>37</span> <span>        <br></span><span>38</span> <span>    }<br></span><span>39</span> <span><br></span><span>40</span> <span>//</span><span> 标准实现</span><span><br></span><span>41</span> <span>static</span><span>public</span><span>function</span><span> gc() {<br></span><span>42</span> <span>        <br></span><span>43</span> <span>    }<br></span><span>44</span> <span><br></span><span>45</span> <span>//</span><span> 标准实现</span><span><br></span><span>46</span> <span>static</span><span>public</span><span>function</span><span> close() {<br></span><span>47</span> <span>//</span><span> 一切OK,再由Session Close的时候,释放数据库连接</span><span><br></span><span>48</span> <span>        DB_Connector</span><span>::</span><span>disconnect(self</span><span>::</span><span>$_connectorKey</span><span>);<br></span><span>49</span> <span>        self</span><span>::</span><span>$_currSess</span><span>=</span><span>null</span><span>;<br></span><span>50</span> <span>    }<br></span><span>51</span> <span>}</span></p>

好了,大功告成!原理就不多说了,多做点测试吧。

 

将Session写成Model的好处是,可以有针对性的进行单元测试。也许有用户会担心,你把Session放在数据库层,能承受得多大的并发量呢?

OK,我可以给出一些实际数据,一个投票的程序,PHP和MySQL跑在同一台服务器(Server系统是Ubuntu Server以Lighttpd,已经通过压力测试优化过fastcgi线程数字)上,3天收集有效投票记录总数900万+(注意,有效投票是指限制ip的,每一票都要检查ip和该ip上一次投票的时间),Session使用Model操作,以MyISAM引擎存放在MySQL的表中,Session主键已经刷到8位数。最高峰一天PV 1200万。

 

另:我发现cnblogs的源代码极其以及十分之丑陋,无法让人家复制代码,提供附件下载。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn