某些用户的会话变量消失(drupal)

时间:2012-04-02 07:44:23

标签: php drupal session

我的网站中存在这个问题困扰了我好几个月。我一直在努力,但现在我似乎无法找到这样的方式,所以我想为什么不解决根本问题?

我的根本问题是,对于我的一些用户,有时(并非总是)会话数据会消失。我知道他们中的大多数肯定都启用了cookie。我想也许这是会议到期的事情,但我不认为就是这样。我将期限设置为很长时间,再加上会话被删除的用户数量太高了......

我正在使用drupal 6。

有什么建议吗?

7 个答案:

答案 0 :(得分:4)

很少有东西,你可能想检查

  1. 您的会话垃圾收集器是如何配置的?请验证PHP设置:session.gc_maxlifetime,session.gc_divisor,session.session.gc_probability。这只是猜测,但GC可能正在删除您的数据。
  2. 数据是否会从会话表中消失?如果对Drupal使用默认会话处理程序,$ _SESSION表的数据将以序列化形式存储在会话表的会话列中。
  3. 如果数据从数据库中消失,并且您可以在开发服务器上重复出现问题,则还可以启用数据库的查询记录(在mysql服务器配置中,有关详细信息,请参阅http://dev.mysql.com/doc/refman/5.1/en/query-log.html)。启用了devel模块后,您可以使用注释(包括有关函数执行查询的信息)来装饰(开发模块设置)您的查询。这可以帮助您找到问题。 不要尝试生产环境
  4. 有时问题可能是由一个已启用的模块引起的,这会使您的$ _SESSION表瘫痪。尝试在启用的模块代码中查找所有$ _SESSION出现,特别是在写入上下文中。一些正则表达式将有助于完成这项任务。

答案 1 :(得分:0)

在脚本顶部添加ob_start()session_start();

答案 2 :(得分:0)

我在这里猜一点,我认为这可能是你的问题/解决方案:

简短回答:尝试将sites / default / settings.php中的session.cookie_lifetime设置为0。

答案很长:会话取决于服务器上的会话数据客户端上的PHP会话cookie。如果您将会话cookie的生命周期设置得太低(Drupal默认为23天,通常不会出现问题),会话可能会在客户端被“销毁”(通过浏览器删除cookie,因为它已过期)。

我曾经遇到过这个问题,因为我将Drupal会话cookie的生命周期从23天更改为更低的,因为出于安全原因而试图限制登录持续时间似乎更“合适”。但是降低会话cookie生命周期并不是解决方案。执行此操作的最佳方法是限制服务器端的会话持续时间。您可以通过将session.gc_maxlifetime(也在Drupal设置中)设置为7200(2小时)来完成此操作。然后,您只需要确保会话垃圾收集运行得足够频繁,以便会话在2小时后可靠地结束。

然后,为了避免在客户端销毁cookie的问题,您可以将session.cookie_lifetime设置为0.这将确保只要用户在网站上,他只会在他停留时退出非活动时间超过session.gc_maxlifetime。最重要的是,当他关闭浏览器时,它也会记录用户输出。默认情况下,Drupal没有做到这一点!!

基本更新:为什么将cookie生存期设置为0是个好主意?因为否则THE SERVER设置客户端需要EXPIRE cookie的日期/时间。 那你能说什么呢?好吧,考虑一下这种情况:如果客户端设置错误(是的,确实发生了,即使大多数计算机通过NTP更新时间,有些也没有)!它可能会比您想象的更早到期,即使到达也是如此。 这是我必须调试一次的确切问题......

因此,如果遇到奇怪的会话问题,请至少尝试使用cookie生命周期0:)

答案 3 :(得分:0)

我认为你的问题在于会话保存路径

请在根目录中创建一个名称(chmod 777 MUST)的文件夹,即放置.php的位置并放置此代码

if(!is_writable(session_save_path())){
    session_save_path(dirname(__FILE__)."/<xyz>");
}

开始会议之前。

当您的会话设置使用文件而不是内存时,还会发生这种情况。

答案 4 :(得分:0)

我曾经遇到过类似的问题,其原因是着名的www。

该网站已通过www.example.com和example.com进行访问,该网站工作正常,直到用户点击了使用oposit域名进行硬编码的链接。然后他切换域/子域,并且apache / php在“新”域上为用户创建了一个新会话。当人们从付款中返回时尤其令人讨厌。

我们的解决方案是始终强制用户仅使用example.com,并将所有请求直接禁用或重定向到www.example.com到example.com。通过这种方式,您永远无法在两者上进行会话。

答案 5 :(得分:0)

我听说过Drupal的session_regenerate_id()问题以及一些特定的PHP版本。您可以尝试禁用此功能并进行测试吗?

答案 6 :(得分:0)

我正在关注该帖子,因为似乎没有任何答案可以解决问题。我在D7(和mod_fcgi)中遇到了类似的问题,因此发现了该线程,其效果不仅限于Drupal 6,Drupal 8中的临时存储服务可能会减轻这种情况。

简而言之,我认为种族状况是根本原因。使用Drupal和其他涉及Ajax的框架构建的网站可以变成复杂的野兽,并发访问会话变量。我首先会使用例如寻找长期运行的请求firefox-developer Edition,流量分析器等。此类内容可能会以块或网站上的其他结构形式引入,其中任何一个都可以访问$ _SESSION。随后,关闭尽可能多的块和菜单,直到问题消失。

此外,您可以在函数dashboard_source = ColumnDataSource(record[:1]) # initialize with first row ecg_source = ColumnDataSource(record[:1]) # initialize with first row # some options time_window = 12 # seconds to keep in view ecg_length = 1 # seconds to keep in view in ECG plot update_rate = 1000 / sampling_rate # number of milliseconds between each plot update # update function in which source data is fed from record dataframe current_record_pos = 1 def update_dashboard_source(): global current_record_pos new_row = record.iloc[current_record_pos] dashboard_source.stream(new_row, rollover = sampling_rate * time_window) ecg_source.stream(new_row, rollover = sampling_rate * ecg_length) current_record_pos += 1 def ecg_dashboard(doc): # dashboard element: ECG plot/s ---- ------- ---- ------- ---- ------- ---- ------- ecg_plot = figure(width=800, height=400, title='ECG', x_axis_label='time in ms', y_range=(-1, 1.5)) # plot ECG channels for record_channel, color in zip(record_channels, ['green', 'blue']): ecg_plot.line(source=ecg_source, x='time_ms', y=record_channel, alpha=.3, legend=record_channel+' ', color=color) # dashboard element: heart rate variability ---- ------- ---- ------- ---- ------- ---- ------- hrv_plot = figure(width=400, height=400, title='heart rate variability', x_axis_label="r'r''", y_axis_label="r''r'''") hrv_plot.circle(source=dashboard_source, x='r_diff_1', y='r_diff_2', size=10, alpha=.23) # gather everything in a dashboard element and add it to the document ecg_row = row(ecg_plot) feature_row = row(hrv_plot) dashboard = column(ecg_row, feature_row) doc.add_root(dashboard) doc.add_periodic_callback(update_dashboard_source, update_rate) show(ecg_dashboard) includes/session.inc中向drupal_session_start()添加一些调试输出。输出应包含服务器进程的进程ID。查找主要内容显示后很长时间发生的提交。

关于可能发生的情况的假设

长时间运行的请求会覆盖旧状态的会话变量。这部分是由于在Drupal中如何管理会话变量。默认情况下,会话数据作为数据的单个序列化字符串表示形式存储在会话表的会话列中。原则上,读写会话变量应该是一个原子动作,但这并不容易执行。以下代码来自session.inc,用于处理会话数据的写入。它使用时间戳记和is_changed标志来有效地延迟写入。也可以说,会话不是线程安全的。另外,它使用SQL MERGE语句,但是不能合并序列化的变量。实际上,最新的书面状态是获胜状态。

drupal_session_commit()

想象一下以下情况:

主要站点内容是一个显示按钮和计数器的模块。计数器存储在$ _SESSION ['counter']中,并通过单击按钮递增。该按钮具有Ajax回调,可有效执行以下操作

    // For performance reasons, do not update the sessions table, unless
    // $_SESSION has changed or more than 180 has passed since the last update.
    if ($is_changed || !isset($user->timestamp) || REQUEST_TIME - $user->timestamp > variable_get('session_write_interval', 180)) {
      // Either ssid or sid or both will be added from $key below.
      $fields = array(
        'uid' => $user->uid,
        'cache' => isset($user->cache) ? $user->cache : 0,
        'hostname' => ip_address(),
        'session' => $value,
        'timestamp' => REQUEST_TIME,
      );

    ...

    db_merge('sessions')
    ->key($key)
    ->fields($fields)
    ->execute();

在网站页脚的一个块中,有一个块加载会话数据,无论出于何种原因,该数据都会停顿。

function counter_increment(){
   if (!drupal_session_started()) {
        // Must initialize sessions for anonymous users.
        drupal_session_start();
   }
   $_SESSION['counter'] = (isset($_SESSION['counter'])) ? $_SESSION['counter']++ : 0;
   drupal_session_commit();
   ...
}   

因此,一旦加载了内容,页脚块就会调用do_nothing,而挂起将暂停180秒以上。同时,用户使用回调增加计数器,但是一旦do_nothing完成,整个会话内容将被do_nothing请求的会话内容替换,从而丢失$ _SESSION ['counter'];。