memcached在JAVA项目中的应用

来源:互联网 发布:淘宝专业版一钻以上 编辑:程序博客网 时间:2024/05/10 19:13

http://hejianke83.blog.163.com/blog/static/607651620135901820833/

一、   Memcache的使用
使用Memcache的网站一般流量都是比较大的,为了缓解数据库的压力,让Memcache作为一个缓存区域,把部分信息保存在内存中,在前端能够迅速的进行存取。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单台Memcache的内存容量的有限的。我这里简单提出我的个人看法,未经实践,权当参考。

二、   分布式应用
Memcache本来支持分布式,我们客户端稍加改造,更好的支持。我们的key可以适当进行有规律的封装,比如以user为主的网站来说,每个用户都有User ID,那么可以按照固定的ID来进行提取和存取,比如1开头的用户保存在第一台Memcache服务器上,以2开头的用户的数据保存在第二胎Mecache服务器上,存取数据都先按照User ID来进行相应的转换和存取。

三、   但是这个有缺点,就是需要对User ID进行判断,如果业务不一致,或者其他类型的应用,可能不是那么合适,那么可以根据自己的实际业务来进行考虑,或者去想更合适的方法。

四、   减少数据库压力
这个算是比较重要的,所有的数据基本上都是保存在数据库当中的,每次频繁的存取数据库,导致数据库性能极具下降,无法同时服务更多的用户,比如MySQL,特别频繁的锁表,那么让Memcache来分担数据库的压力吧。我们需要一种改动比较小,并且能够不会大规模改变前端的方式来进行改变目前的架构。

五、   我考虑的一种简单方法:
后端的数据库操作模块,把所有的Select操作提取出来(update/delete/insert不管),然后把对应的SQL进行相应的hash算法计算得出一个hash数据key(比如MD5或者SHA),然后把这个key去Memcache中查找数据,如果这个数据不存在,说明还没写入到缓存中,那么从数据库把数据提取出来,一个是数组类格式,然后把数据在set到Memcache中,key就是这个SQL的hash值,然后相应的设置一个失效时间,比如一个小时,那么一个小时中的数据都是从缓存中提取的,有效减少数据库的压力。缺点是数据不实时,当数据做了修改以后,无法实时到前端显示,并且还有可能对内存占用比较大,毕竟每次select出来的数据数量可能比较巨大,这个是需要考虑的因素。

六、   Memcache的安全
我们上面的Memcache服务器端都是直接通过客户端连接后直接操作,没有任何的验证过程,这样如果服务器是直接暴露在互联网上的话是比较危险,轻则数据泄露被其他无关人员查看,重则服务器被入侵,因为Mecache是以root权限运行的,况且里面可能存在一些我们未知的bug或者是缓冲区溢出的情况,这些都是我们未知的,所以危险性是可以预见的。为了安全起见,我做两点建议,能够稍微的防止黑客的入侵或者数据的泄露。

七、   内网访问
最好把两台服务器之间的访问是内网形态的,一般是Web服务器跟Memcache服务器之间。普遍的服务器都是有两块网卡,一块指向互联网,一块指向内网,那么就让Web服务器通过内网的网卡来访问Memcache服务器,我们Memcache的服务器上启动的时候就监听内网的IP地址和端口,内网间的访问能够有效阻止其他非法的访问。
# memcached -d -m 1024 -u root -l 192.168.0.200 -p 11211 -c 1024 -P/tmp/memcached.pid
Memcache服务器端设置监听通过内网的192.168.0.200的ip的11211端口,占用1024MB内存,并且允许最大1024个并发连接

八、   设置防火墙
防火墙是简单有效的方式,如果却是两台服务器都是挂在网的,并且需要通过外网IP来访问Memcache的话,那么可以考虑使用防火墙或者代理程序来过滤非法访问。
一般我们在Linux下可以使用iptables或者FreeBSD下的ipfw来指定一些规则防止一些非法的访问,比如我们可以设置只允许我们的Web服务器来访问我们Memcache服务器,同时阻止其他的访问。
# iptables -F
# iptables -P INPUT DROP
# iptables -A INPUT -p tcp -s 192.168.0.2 –dport 11211 -j ACCEPT
# iptables -A INPUT -p udp -s 192.168.0.2 –dport 11211 -j ACCEPT
上面的iptables规则就是只允许192.168.0.2这台Web服务器对Memcache服务器的访问,能够有效的阻止一些非法访问,相应的也可以增加一些其他的规则来加强安全性,这个可以根据自己的需要来做。

九、    

 

十、memcached安装与运行

下载地址:http://libevent.org/

比如:https://github.com/downloads/libevent/libevent/libevent-2.0.20-stable.tar.gz

安装libevent如果显示没有安装gcc可以参考使用yum安装gcc http://www.linuxidc.com/Linux/2012-08/69360.htm ,然后再安装libevent

下载完成上传到linux上之后,编译、安装:

1.1./configure --prefix=/usr

1.2 编译make

1.3 编译&安装 make install

1.4查看是否已经安装:

ls -al/usr/lib | grep libevent

十一、       安装memcache

下载地址:code.google.com/p/memcached/downloads/list

2.1./configure --with-libevent=/usr

2.2 编译make

2.3 编译&安装 make install

2.4运行

#/usr/local/bin/memcached -d -m 256 -u root -l 192.168.1.1 -p 11211 -c 256 -P/opt/memcached/pid.pid

-d选项是启动一个守护进程,

-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB

-u是运行Memcache的用户,我这里是root

-l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200

-p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口,

-c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定,

-P是设置保存Memcachepid文件,我这里是保存在 /opt/memcached/pid.pid

2.5结束Memcache进程,执行:

# kill'cat /opt/memcached/pid.pid'

也可以启动多个守护进程,不过端口不能重复

 

十二、       memcache客户端开发与应用

a)         在服务端启动并开启监听数据操作情况下,通过建立客户端对数据进行存取操作,完成数据的缓存写入和读取处理。

b)         下载客户端软件JAR包(slf4j-simple-1.6.1.jar,java_memcached-release_2.6.6.jar),建立针对用户信息数据进行缓存处理,定时更新实体类:

package com.gocheck.common.jobtask;

 

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

 

import org.apache.log4j.Logger;

 

import com.danga.MemCached.MemCachedClient;

import com.danga.MemCached.SockIOPool;

import com.gocheck.check.bean.DocumentBean;

import com.gocheck.common.basic.MyException;

import com.gocheck.crm.bean.CustomerBean;

import com.gocheck.crm.manager.AccountManager;

import com.gocheck.crm.manager.CustomerManager;

import com.gocheck.crm.manager.LoginInfoManager;

import com.gocheck.crm.manager.OrgManager;

import com.gocheck.param.ParamConstants;

import com.gocheck.param.manager.ParamUtils;

 

/**

 *     memcache定时更新缓存客户端实体类

 *  @project:

 *  @class:MemcacheTaskJob.java

 *  @author:heguanhua E-mail:37809893@qq.com

 *  @date:Jun 6, 2013 4:48:01 PM

 */

public class MemcacheTaskJob {

         privatestatic Logger log = Logger.getLogger(DocumentSyncJob.class);

    privateCustomerManager customerManager;

    privateCustomerBean customerBean;

    privateLoginInfoManager loginInfoManager;

    // 创建全局的唯一实例 

    protectedstatic MemCachedClient mcc = new MemCachedClient(); 

    protectedstatic MemcacheTaskJob memCachedManager = new MemcacheTaskJob(); 

   

    // 设置与缓存服务器的连接池 

    static{ 

        // 服务器列表和其权重 

 

                   Stringmemcache_host = ParamUtils.getParamValue(ParamConstants.MEMCACHE_HOST_PORT,"");

        //服务器监听IP:端口

       String[] servers = { memcache_host }; 

       Integer[] weights = { 1 }; 

 

        // 获取socke连接池的实例对象 

       SockIOPool pool = SockIOPool.getInstance(); 

 

        // 设置服务器信息 

       pool.setServers(servers); 

       pool.setWeights(weights); 

 

        // 设置初始连接数、最小和最大连接数以及最大处理时间 

       pool.setInitConn(50); 

       pool.setMinConn(50); 

        pool.setMaxConn(1000); 

       pool.setMaxIdle(1000 * 60 * 60 * 6); 

 

        // 设置主线程的睡眠时间 

       pool.setMaintSleep(30); 

 

        // 设置TCP的参数,连接超时等 

       pool.setNagle(false); 

       pool.setSocketTO(3000); 

       pool.setSocketConnectTO(0); 

 

        // 初始化连接池 

       pool.initialize(); 

    } 

 

    /**

     * 保护型构造方法,不允许实例化!

     * 

     */ 

    protectedMemcacheTaskJob() {} 

 

    /**

     * 获取唯一实例.

     * 

     *@return

     */ 

    publicstatic MemcacheTaskJob getInstance() { 

       return memCachedManager; 

    } 

   

   

         /**

          * 执行定时任务每1天执行一次用户与账户数据查询,并保存到memcache服务器中,同时清空前一天的memcache数据缓存。

          * @throws MyException

          */

         publicvoid executeSearch(){

                   try{

                            /**

                             * 查询最近一周访问过系统的用户CUSTID列表存储到memcache中。

                             *     where  DATE_SUB(CURDATE(), INTERVAL 7 DAY) <=date(logindate)

                             *     #最近一个月

                             *     whereDATE_SUB(CURDATE(), INTERVAL INTERVAL 1 MONTH) <= date(logindate);

                             */

                            Stringmemcache_close = ParamUtils.getParamValue(ParamConstants.MEMCACHE_CLOSE,"");

                            List<CustomerBean>ul = new ArrayList<CustomerBean>();

                            if(memcache_close!= null && Integer.valueOf(memcache_close) == 1){

                                     ul= customerManager.findMemcacheUserList();

                                     if(mcc.get("customerList")== null){

                                              //log.info("newadd memcache");

                                               mcc.add("customerList",ul);

                                     }else{

                                               //log.info("replacememcache");

                                               mcc.replace("customerList",ul);

                                     }

                            }else{

                                     mcc.add("customerList",ul);

                            }

                            //删除一周以前的登陆用户信息

                 loginInfoManager.delBySession();

                            log.info("定时更新memcache中活跃用户数据,是否存在数据列表:"+mcc.keyExists("customerList")+",存在的活跃用户数:"+((List)mcc.get("customerList")).size());

                   }catch (Exception e) {

                            e.printStackTrace();

                   }

 

         }

   

   

 

    /**

     * 添加一个指定的值到缓存中.

     * 

     * @paramkey

     * @paramvalue

     *@return

     */ 

    publicboolean add(String key, Object value) { 

       return mcc.add(key, value); 

    } 

 

    publicboolean add(String key, Object value, Date expiry) { 

       return mcc.add(key, value, expiry); 

    } 

 

    publicboolean replace(String key, Object value) { 

       return mcc.replace(key, value); 

    } 

 

    publicboolean replace(String key, Object value, Date expiry) { 

       return mcc.replace(key, value, expiry); 

    } 

 

    /**

     * 根据指定的关键字获取对象.

     * 

     * @paramkey

     *@return

     */ 

    publicObject get(String key) { 

       return mcc.get(key); 

    } 

 

    publicstatic void main(String[] args) { 

             MemcacheTaskJob cache =MemcacheTaskJob.getInstance(); 

       cache.add("hello", 234); 

       System.out.print("get value : " +cache.get("hello")); 

    }

 

         publicCustomerManager getCustomerManager() {

                   returncustomerManager;

         }

 

         publicvoid setCustomerManager(CustomerManager customerManager) {

                   this.customerManager= customerManager;

         }

 

         publicCustomerBean getCustomerBean() {

                   returncustomerBean;

         }

 

         publicvoid setCustomerBean(CustomerBean customerBean) {

                   this.customerBean= customerBean;

         }

 

         publicstatic MemcacheTaskJob getMemCachedManager() {

                   returnmemCachedManager;

         }

 

         publicstatic void setMemCachedManager(MemcacheTaskJob memCachedManager) {

                   MemcacheTaskJob.memCachedManager= memCachedManager;

         }

 

         publicLoginInfoManager getLoginInfoManager() {

                   returnloginInfoManager;

         }

 

         publicvoid setLoginInfoManager(LoginInfoManager loginInfoManager) {

                   this.loginInfoManager= loginInfoManager;

         } 

   

   

}

c)        新用户操作方法:

       /**

         * 把最新注册用户添加到memcache的缓存中

         */

    MemcacheTaskJobcache = MemcacheTaskJob.getInstance();

    if(cache.get("customerList") != null){

        //增加到memcache中去

        customerBean.setFreeIntegral(accountBean.getFreeIntegral());

        List<CustomerBean>cl = (List<CustomerBean>) cache.get("customerList");

        cl.add(customerBean);

        cache.replace("customerList", cl);

        log.info("register user "+customerBean.getLoginName()+" replacememcache!");

       }

0 0
原创粉丝点击