• Memcached 两款.NET客户端的郁闷事儿


    不久以后就要负责一个比较大的项目,有多大?反正就是挺大的。现在处于筹备阶段,我主要负责系统框架搭建,在系统缓存这一块决定采用Http运行时缓存+memcached。

      memcached 以前用过几次 不过也是小打小闹型,尚未正式大型应用过。这次也算是个难得的练手机会吧。memcached服务器打算分布在web应用服务器以及数据库服务器上。

    (两台服务器有点花大手笔了 两台Dell PowerEdge R810 自定义了配置 大约单台6.5万)。

      关于Memcached的.NET 客户端的选择这一块,主要有两款候选库,Memcached.ClientLibrary(discuz .net版本企业版使用的缓存客户端) 以及 Enyim 。

    这两款类库都比较好用,后者使用更加方便,只需简单的配置。我选择了Enyim。

      在测试的时候发现了一个共同的问题,假如在使用多个Memcached服务时,当其中一台服务器网络不通(其它至少存在一个可用的Memcached服务)的情况下,

    Memcached.ClientLibrary 的缓存功能失效,Enyim 在缓存操作的时候实例化socket对象时没有进行连接超时的处理(Memcached.ClientLibrary处理了)。

    一般都要等待10秒以上线程才能继续,线程被卡住不放了。在以往的应用中都是 memcached 与 web 共存在一台服务器上,所以没遇到过这个问题,

    但是这次打算 web 服务器上开一个memcached实例,数据库服务器上开一个memcached实例,所以才关注到了这个问题。一般情况下这个问题也不会有太大影响,

    但是心里总是感觉不爽。不打算使用Memcached.ClientLibrary ,所以找了 Enyim 的源代码,打算自己修改一下。

      翻看了一下源代码, Enyim 的问题主要存在于 Enyim.Caching.Memcached.PooledSocket,该类主要用于创建socket 连接 。

      以下是构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    internal PooledSocket(IPEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout, Action<PooledSocket> cleanupCallback)
     
             {
     
                  this.endpoint = endpoint;
     
                  this.cleanupCallback = cleanupCallback;
     
      
     
                  this.socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
     
      
     
                  this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, connectionTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)connectionTimeout.TotalMilliseconds);
     
                  this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeout == TimeSpan.MaxValue ? Timeout.Infinite : (int)receiveTimeout.TotalMilliseconds);
     
      
     
                  // all operations are "atomic", we do not send small chunks of data
     
                  this.socket.NoDelay = true;
     
      
     
                  this.socket.Connect(endpoint);
     
                  this.inputStream = new BufferedStream(new BasicNetworkStream(this.socket));
     
         }

      由于Socket (我对Socket没啥研究 几乎就是文盲)对象没法设置连接超时之类的属性(这下怎么办呀),并且在源代码中可以看出 this.socket.Connect(endpoint)

    的时候没有做任何处理,导致当连接有问题的服务时出现了较长时间的线程等待。我在网上找啊找,找到了类似问题的解决方案,就是使用线程池。

    网上找的代码修修改改 差不多能用了,创建了 Enyim.Caching.Memcached.SocketConnector,其中有一个核心的静态方法,用来创建Socket对象,并且运行定义超时时间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public static Socket GetConnectdSocket(IPEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout, intmillisecondsTimeout)
     
            {
     
                ConnectorState state = new ConnectorState();
     
                state.Endpoint = endpoint;
     
                state.ConnectionTimeout = connectionTimeout;
     
                state.ReceiveTimeout = receiveTimeout;
     
                ThreadPool.QueueUserWorkItem(new WaitCallback(SocketConnector.ConnectThreaded), state);
     
                if (state.Completed.WaitOne(millisecondsTimeout, false))
     
                {
     
                    if (state.Socket == null)
     
                    {
     
                        throw state.Exception;
     
                    }
     
                    return state.Socket;
     
                }
     
                state.Abort();
     
                throw new SocketException(0x2af9);
     
            }

    然后修改PooledSocket的构造为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    internal PooledSocket(IPEndPoint endpoint, TimeSpan connectionTimeout, TimeSpan receiveTimeout, Action<PooledSocket> cleanupCallback)
     
            {
     
                this.endpoint = endpoint;
     
                this.cleanupCallback = cleanupCallback;
     
                this.socket = SocketConnector.GetConnectdSocket(endpoint, connectionTimeout, receiveTimeout, 100);
     
                this.inputStream = new BufferedStream(new BasicNetworkStream(this.socket));
     
            }

    我将连接超时设置为100毫秒,那个恶心的问题基本算是解决了。

  • 相关阅读:
    Spring Web Flow 简介
    LeetCode:按序打印【1114】
    Java基础教程:多线程基础(5)——倒计时器(CountDownLatch)
    React:快速上手(8)——前后端分离的跨域访问与会话保持
    SpringBoot学习笔记:自定义拦截器
    Java进阶教程:垃圾回收
    SpringMVC:学习笔记(12)——ThreadLocal实现会话共享
    Node.js学习笔记(4):Yarn简明教程
    Docker:学习笔记(1)——核心概念及Ubuntu安装
    Java基础教程:内部类
  • 原文地址:https://www.cnblogs.com/Alex80/p/4422381.html
Copyright © 2020-2023  润新知