精华内容
下载资源
问答
  • 缓存策略

    2020-12-15 09:18:11
  • nginx 缓存策略

    2021-04-04 21:16:50
    nginx 缓存策略nginx 缓存策略1、 传统缓存策略2、 高效缓存 nginx 缓存策略 这里介绍两种策略: 1、 传统缓存策略: client -> nginx -> fastcgi_pass -> php-fpm:9000 -> memcache -> memcached:...

    nginx 缓存策略

    这里介绍两种策略:

    1、 传统缓存策略: client -> nginx -> fastcgi_pass -> php-fpm:9000 -> memcache -> memcached:11211

    缺点:传统缓存策略难以体现 nginx 高并发的特点,因为当 nginx 缓存中没有客户访问请求的响应时,交给 php-fpm 处理,一旦交给后端,nginx 会一直处于等待响应状态,直到后端处理完成,返回响应
    在这里插入图片描述

    2、高效缓存策略:client -> nginx -> memcache -> memcached:11211
    在这里插入图片描述
    主要结合 memcache(应用加速方式 memcache 模块 -> memcached:11211 服务)

    什么是Memcached?
    本质上是通用的免费和开放源代码,高性能,分布式内存对象缓存系统,但旨在通过减轻数据库负载来加速动态Web应用程序。

    Memcached是一个内存中键值存储,用于存储来自数据库调用,API调用或页面呈现结果的任意小数据块(字符串,对象)。

    Memcached简单而强大。其简单的设计可促进快速部署,易于开发,并解决了大型数据缓存面临的许多问题。它的API适用于大多数流行语言。

    1、 传统缓存策略

    client -> nginx -> fastcgi_pass -> php-fpm:9000 -> memcache -> memcached:11211

    # 编译,安装 PHP 的 memcache 模块
    tar zxf memcache-4.0.5.2.tgz 
    cd memcache-4.0.5.2
    
    # 安装 memcache 依赖性
    yum install -y autoconf
    
    # phpize 用于安装php拓展,生成 ./configure 文件
    phpize
    
    # 添加 memcache 模块
    ./configure --enable-memcache
    make
    make install
    
    # 配置 PHP 主配置文件,启用 memcache 模块
    vim /usr/local/php/etc/php.ini
    	 913 extension=memcache 
    
    # 重载 php-fpm 
    systemctl reload php-fpm
    php -m | grep memcache
    
    # 安装 memcached 服务,默认监控 11211 端口
    yum install -y memcached
    systemctl start memcached
    netstat -anltp | grep memcached
    
    # memcached 服务配置文件
    cat /etc/sysconfig/memcached 
    
    # memcache 解压目录中包含,测试文件和监控文件,通过 nginx 发布出去,就可测试和监控。
    cd memcache-4.0.5.2
    #  测试文件
    cp example.php /usr/local/nginx/html/
    #  web监控文件
    cp memcache.php /usr/local/nginx/html/
    
    # memcached 的web监控文件配置
    vim /usr/local/nginx/html/memcache.php
    ……
    define('ADMIN_USERNAME','admin');       // Admin Username
    define('ADMIN_PASSWORD','westos');      // Admin Password
    ……
    $MEMCACHE_SERVERS[] = 'localhost:11211'; // add more as an array
    #$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
    ……
    

    示例截图
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述测试:
    在这里插入图片描述在这里插入图片描述

    2、 高效缓存

    client -> nginx -> memcache -> memcached:11211

    由于之前的 nginx 是源码安装,这里用 openrestry 演示 nginx 的高效缓存。

    OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    #  openrestry:
    
    
    # 先停止已开启的nginx 服务
    systemctl stop nginx
    
    # 安装 openresty
    tar axf openresty-1.19.3.1.tar.gz 
    cd openresty-1.19.3.1
    ./configure 
    gmake
    gmake install
    
    #  配置openresty 中 nginx 配置文件
    vim /usr/local/openresty/nginx/conf/nginx.conf
    ……
    user  nginx nginx;
    worker_processes  1;
    ……
                index  index.php index.html index.htm;
    ……
            location ~ \.php$ {
                root           html;
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                #fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
                include        fastcgi.conf;
            }
    ……
    
    
    #  检测语法
    /usr/local/openresty/nginx/sbin/nginx -t
    #  启动服务
    /usr/local/openresty/nginx/sbin/nginx
    #  检测端口
    netstat -anltp | grep 80
    
    

    nginx + memcache 高效缓存

    #  nginx 高效缓存:
    
    #  拷贝测试文件
    cd /usr/local/openresty/nginx/html
    cp /root/memcache-4.0.5.2/example.php .
    
    #  配置 nginx 服务
    vim /usr/local/openresty/nginx/conf/nginx.conf
    #  定义负载均衡器,这里只有 server1 一台服务器,定义一台主机。
    ……
    http {
    ……
             upstream memcache {
             server 127.0.0.1:11211;
             keepalive 512;
             }
    ……
    # 定义内部访问 memcahce
             location /memc {
             internal;
             memc_connect_timeout 100ms;
             memc_send_timeout 100ms;
             memc_read_timeout 100ms;
             set $memc_key $query_string;
             set $memc_exptime 300;
             memc_pass memcache;
             }
    ……
    # 定义访问策略(client 访问时,先在nginx 缓存中找,没有再访问后面策略,访问到后存到nginx 缓存,下次直接从nginx 缓存中获取)
             location ~ \.php$ {
                 set $key $uri$args;
                 srcache_fetch GET /memc $key;
                 srcache_store PUT /memc $key;
    ……
    
    #  检测语法
    /usr/local/openresty/nginx/sbin/nginx -t
    #  重载 nginx 服务 
    /usr/local/openresty/nginx/sbin/nginx -s reload
    
    测试:
    ab -c10 -n10000  http://172.25.9.1/example.php
    
    

    示例截图
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述测试
    在这里插入图片描述在这里插入图片描述

    展开全文
  • 安卓中的缓存包括两种情况即内存缓存与磁盘缓存,其中内存缓存主要是使用LruCache这个类,其中内存缓存我在【安卓中的缓存策略系列】安卓缓存策略之内存缓存LruCache中已经进行过详细讲解,如看官还没看过此博客,...

    安卓中的缓存包括两种情况即内存缓存与磁盘缓存,其中内存缓存主要是使用LruCache这个类,其中内存缓存我在【安卓中的缓存策略系列】安卓缓存策略之内存缓存LruCache中已经进行过详细讲解,如看官还没看过此博客,建议看官先去看一下。

    我们知道LruCache可以让我们快速的从内存中获取用户最近使用过的Bitmap,但是我们无法保证最近访问过的Bitmap都能够保存在缓存中,像类似GridView等需要大量数据填充的控件很容易就会用完整个内存缓存。另外,我们的应用可能会被类似打电话等行为而暂停导致退到后台,因为后台应用可能会被杀死,那么内存缓存就会被销毁,缓存的Bitmap也就不存在了。一旦用户恢复应用的状态,那么应用就需要重新处理那些图片,另外某些情况下即使用户退出整个APP后重新打开该APP其缓存的图片应该还能被显示出来,显然此种情况下使用内存缓存是做不到的。

    而磁盘缓存可以用来保存那些已经处理过的Bitmap,它还可以减少那些不在内存缓存中的Bitmap的加载次数。磁盘缓存主要涉及到DiskLruCache这个类。下面从源码的角度详细讲解DiskLruCache这个类,然后在此基础上讲解如何使用DiskLruCache,让读者知其然更知其所以然。


    一DiskLruCache类:

    首先我们来看一下其构造函数

     private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
            this.directory = directory;
            this.appVersion = appVersion;
            this.journalFile = new File(directory, JOURNAL_FILE);
            this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
            this.valueCount = valueCount;
            this.maxSize = maxSize;
        }
    
    可以看到其构造函数被private修饰,也就意味着对外是不可见的,即我们不能通过其构造函数来创建一个DiskLruCache对象,如果要创建一个DiskLruCache实例需要使用open函数,其代码如下:

     public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
                throws IOException {
            if (maxSize <= 0) {
                throw new IllegalArgumentException("maxSize <= 0");
            }
            if (valueCount <= 0) {
                throw new IllegalArgumentException("valueCount <= 0");
            }
    
            // prefer to pick up where we left off
            DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
            if (cache.journalFile.exists()) {
                try {
                    cache.readJournal();
                    cache.processJournal();
                    cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true),
                            IO_BUFFER_SIZE);
                    return cache;
                } catch (IOException journalIsCorrupt) {
    //                System.logW("DiskLruCache " + directory + " is corrupt: "
    //                        + journalIsCorrupt.getMessage() + ", removing");
                    cache.delete();
                }
            }
    
            // create a new empty cache
            directory.mkdirs();
            cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
            cache.rebuildJournal();
            return cache;
        }
    可以看到open函数对应的参数与DiskLruCache的参数完全一致,其中第一个参数directory表示磁盘缓存在文件系统中的存储路径,一般选择SD卡上的缓存路径,默认位置为/sdcard/Android/data/<application package_name>/cache目录下,其中<application package_name>表示应用的包名,当应用被卸载后该目录会被删除。第二个参数顾名思义为app版本号,通常将其置为1,当版本号被改变时会清空之前所有的缓存文件,第三个参数用来指定单个缓存节点可以对应的缓存文件个数,通常为1,第四个参数maxSize顾名思义表示该磁盘缓存的最大容量。

    其中第一个参数maxSize也可以指定选择data下的当前应用的目录(此时的缓存路径为/data/data/<application package>/cache),所以通常我们先判断是否存在SD卡,如果存在则使用SD卡缓存,否则选择data下的当前应用的目录缓存。具体代码如下:

    public File getDiskCacheDir(Context context, String uniqueName) {
    	String cachePath;
    	if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
    			|| !Environment.isExternalStorageRemovable()) {
    		cachePath = context.getExternalCacheDir().getPath();
    	} else {
    		cachePath = context.getCacheDir().getPath();
    	}
    	return new File(cachePath + File.separator + uniqueName);
    }
    

    在open函数中可以看到首先会调用DiskLruCache的构造函数,在该构造函数中创建了journalFile,journalFileTmp这两个文件,然后判断journalFile是否存在,如果存在则
    调用cache.readJournal();读取journal日志文件,然后调用 cache.processJournal();处理日志文件,该函数的作用就是计算初始化的大小和收集缓存文件中的垃圾文件( Computes the initial size and collects garbage as a part of opening the cache),删除Dirty记录(Dirty entries are assumed to be inconsistent and will be deleted),即垃圾文件.这个概念与数据库中的读取脏数据是差不多的,讲到这里就不得不为读者讲解一下DiskLruCache的日志文件的格式。格式如下(注:此图来源于网络,向贡献该图的人表示感谢)


    其中的前五行基本上是固定的,表示DiskLruCache日志文件的头部数据,第一行是个固定的字符串“libcore.io.DiskLruCache”,意味着我们使用的是DiskLruCache,

    第二行是DiskLruCache的版本号,这个值是恒为1的。第三行是应用程序的版本号,这个值与我们在open()方法里传入的版本号是相同的。第四行是valueCount,这个值也是在open()方法中传入的,通常情况下都为1。第五行是一个空行。空行过后才是日志文件的内容:

    接下来是一个以DIRTY开头的行,其后的一串数字表示的是存入的数据的key,如果读者了解数据库的话,知道一般DIRTY表示的是脏数据,这是因为当我们每次向磁盘缓存中写入一条数据时都会向journal文件中写入一条DIRTY记录,表示我们正准备写入一条缓存数据,但不知结果如何。当调用commit()方法表示写入缓存成功,这时会向journal中写入一条CLEAN记录,意味着这条“脏”数据被“洗干净”,它不再是脏数据,当调用abort()方法表示写入缓存失败,这时会向journal中写入一条REMOVE记录。也就是说,每一行DIRTY的key,后面都应该有一行对应的CLEAN或者REMOVE的记录,否则这条数据就是“脏”的,会被自动删除掉。另外以READ开头的行表示我们从缓存中读取了一条数据,这时会向日志文件中添加一个READ记录。

    这样我们就可以理解上面讲述的 cache.processJournal()函数处理日志文件的过程,即该函数会清除只出现DIRTY但未出现CLEAN或REMOVE的记录,即出现CLEAN且没被REMOVE的记录才会保存下来,然后通过cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true), IO_BUFFER_SIZE);将CLEAN的记录保存到日志文件中,最后返回该cache。


    第二种情况是如果cache.journalFile不存在,相当于初次创建cahce文件,则会创建一个空的cache,代码如下:

            directory.mkdirs();
            cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
            cache.rebuildJournal();
    在创建空的Cahce时会调用 cache.rebuildJournal();的方法,该方法的作用是删除日志文件中的多余信息,如果日志文件已存在,则会替换当前的日志文件。在该过程中会向日志文件中写入头部数据,这也是为何我们在上面看到的DiskLruCache的日志文件的格式中会包含前面5行数据的原因,代码如下:

     private synchronized void rebuildJournal() throws IOException {
            if (journalWriter != null) {
                journalWriter.close();
            }
    
            Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE);
            writer.write(MAGIC);
            writer.write("\n");
            writer.write(VERSION_1);
            writer.write("\n");
            writer.write(Integer.toString(appVersion));
            writer.write("\n");
            writer.write(Integer.toString(valueCount));
            writer.write("\n");
            writer.write("\n");
    
            for (Entry entry : lruEntries.values()) {
                if (entry.currentEditor != null) {
                    writer.write(DIRTY + ' ' + entry.key + '\n');
                } else {
                    writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
                }
            }
    
            writer.close();
            journalFileTmp.renameTo(journalFile);
            journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE);
        }

    接下来我们看一下DiskLruCache中的重要方法:

    首先来看一下关于添加缓存的edit方法。

     public Editor edit(String key) throws IOException {
            return edit(key, ANY_SEQUENCE_NUMBER);
        }
    
        private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
            checkNotClosed();
            validateKey(key);
            Entry entry = lruEntries.get(key);
            if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER
                    && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) {
                return null; // snapshot is stale
            }
            if (entry == null) {
                entry = new Entry(key);
                lruEntries.put(key, entry);
            } else if (entry.currentEditor != null) {
                return null; // another edit is in progress
            }
    
            Editor editor = new Editor(entry);
            entry.currentEditor = editor;
    
            // flush the journal before creating files to prevent file leaks
            journalWriter.write(DIRTY + ' ' + key + '\n');
            journalWriter.flush();
            return editor;
        }
    可以看到edit方法是同步的。edit方法中首先会调用validateKey(key);来检测传入的key是否合法,不能包含空格,换行,当我们在缓存一张图片时通常我们拿到的是图片的Url,而Url中可能会包含上述不合法字符,所以通常我们会将图片的Url转换为key,然后将其作为参数传给edit(),然后调用LinkedHashMap的get方法通过key获取缓存entry,如果该entry为空(表示我们第一次存入该缓存),则会通过key创建Entry将其赋给entry然后将其put到lruEntries,即entry = new Entry(key);lruEntries.put(key, entry);

    如果获取的entry不为空,则代表不是初次存入该key的缓存,则判断entry.currentEditor是否为空,如果不为空则表示当前缓存entry正在被edit,此时将直接返回null,即DiskLruCache不允许同时edit一个缓存对象。注意entry.currentEditor不为空的前提是entry不为空。

    如果如果获取的entry不为空同时entry.currentEditor为空,则会根据entyr构造Editor对象editor,然后将该editor的值赋给entry.currentEditor,然后调用journalWriter.write(DIRTY + ' ' + key + '\n');向日志文件中写入一个DIRTY行,表示该记录正在被操作。最后返回该editor。通过该editor的 public OutputStream newOutputStream(int index)方法可以得到缓存文件输出流。通过该文件输出流就可以将缓存写入到磁盘上保存起来,最后必须调用editor的commit()来提交写入操作,这样才真真正正的把记录写入到磁盘缓存上了。


    再来看一下获取缓存的get方法:

     public synchronized Snapshot get(String key) throws IOException {
            checkNotClosed();
            validateKey(key);
            Entry entry = lruEntries.get(key);
            if (entry == null) {
                return null;
            }
    
    
            if (!entry.readable) {
                return null;
            }
    
    
            /*
             * Open all streams eagerly to guarantee that we see a single published
             * snapshot. If we opened streams lazily then the streams could come
             * from different edits.
             */
            InputStream[] ins = new InputStream[valueCount];
            try {
                for (int i = 0; i < valueCount; i++) {
                    ins[i] = new FileInputStream(entry.getCleanFile(i));
                }
            } catch (FileNotFoundException e) {
                // a file must have been deleted manually!
                return null;
            }
    
    
            redundantOpCount++;
            journalWriter.append(READ + ' ' + key + '\n');
            if (journalRebuildRequired()) {
                executorService.submit(cleanupCallable);
            }
    
    
            return new Snapshot(key, entry.sequenceNumber, ins);
        }
    

    同样可以看到get方法也是同步的,它的作用就是根据key返回一个Snapshot对象,可以看到在该方法中同样先调用 validateKey(key);进行合法性检测,如果合法则通过key获取缓存entry,如果entry为空或当前不可读则返回null,否则根据valueCountd的值创建valueCount个文件输入流,这些文件输入流的源即为entry中CLEAN记录的缓存,即 ins[i] = new FileInputStream(entry.getCleanFile(i));然后调用journalWriter.append(READ + ' ' + key + '\n');向缓存日志文件中写入一个READ记录行,最后通过key和文件输入流数组来构造一个Snapshot对象,将其返回。当该值返回后会将其移动到缓存队列的头部(If a value is returned, it is moved to the head of the LRU queue)


    得到Snapshot对象后,通过该对象的public InputStream getInputStream(int index)方法可以获取到缓存的文件输入流,通过该文件输入流即可将缓存的记录转换为Bitmap对象。



    二DiskLruCache的使用

    同样DiskLruCache的使用也主要包括上个模块,即创建磁盘缓存,向磁盘缓存中添加记录,从缓存中获取记录。下面先简单介绍这三个模块的使用,然后结合LruCache和DiskLruCache给出安卓缓存策略的完整代码。

    创建缓存:创建缓存主要使用的是open函数:public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

    public File getDiskCacheDir(Context context, String uniqueName) {
    	String cachePath;
    	if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
    			|| !Environment.isExternalStorageRemovable()) {
    		cachePath = context.getExternalCacheDir().getPath();
    	} else {
    		cachePath = context.getCacheDir().getPath();
    	}
    	return new File(cachePath + File.separator + uniqueName);
    }
    
    	public int getAppVersion(Context context) {
    		try {
    			PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
    			return info.versionCode;
    		} catch (NameNotFoundException e) {
    			e.printStackTrace();
    		}
    		return 1;
    	}
    
    
         DiskLruCache mDiskLruCache = null;
         try {
    	File cacheDir = getDiskCacheDir(context, "bitmap");
    	if (!cacheDir.exists()) {
    		cacheDir.mkdirs();
    	}
    	mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
           } catch (IOException e) {
    	e.printStackTrace();
          }
    
    其中getDiskCacheDir函数用来获取缓存的路径,将其返回值作为参数传给open函数的第一个参数,getAppVersion用来获取App的版本号,将其返回值作为参数传给open函数的第二个参数,第三个参数一般置为1,第四个参数一般指定为10M即可。

    写入缓存:写入缓存主要是通过DiskLruCache.Editor类来完成的,该类是通过DiskLruCache的edit()方法来获取的。通常写入磁盘缓存是从网络上获取然后写入缓存的,因此我们得定义一个线程从网络上获取图片。

    public String hashKeyFromUrl(String key) {
    	String cacheKey;
    	try {
    		final MessageDigest mDigest = MessageDigest.getInstance("MD5");
    		mDigest.update(key.getBytes());
    		cacheKey = bytesToHexString(mDigest.digest());
    	} catch (NoSuchAlgorithmException e) {
    		cacheKey = String.valueOf(key.hashCode());
    	}
    	return cacheKey;
    }
    
    private String bytesToHexString(byte[] bytes) {
    	StringBuilder sb = new StringBuilder();
    	for (int i = 0; i < bytes.length; i++) {
    		String hex = Integer.toHexString(0xFF & bytes[i]);
    		if (hex.length() == 1) {
    			sb.append('0');
    		}
    		sb.append(hex);
    	}
    	return sb.toString();
    }
    
    
    new Thread(){
    	@Override
    	public void run() {
    		try {
    			String imageUrl = "http://www.baidu.com/logo.jpg";
    			String key = hashKeyFromUrl(imageUrl);
    			DiskLruCache.Editor editor = mDiskLruCache.edit(key);
    			if (editor != null) {
    				OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
    				if (downloadUrlToStream(imageUrl, outputStream)) {
    					editor.commit();
    				} else {
    					editor.abort();
    				}
    			}
    			mDiskLruCache.flush();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }.start();
    
    private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
    	HttpURLConnection urlConnection = null;
    	BufferedOutputStream out = null;
    	BufferedInputStream in = null;
    	try {
    		final URL url = new URL(urlString);
    		urlConnection = (HttpURLConnection) url.openConnection();
    		in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
    		out = new BufferedOutputStream(outputStream, 8 * 1024);
    		int b;
    		while ((b = in.read()) != -1) {
    			out.write(b);
    		}
    		return true;
    	} catch (final IOException e) {
    		e.printStackTrace();
    	} finally {
    		if (urlConnection != null) {
    			urlConnection.disconnect();
    		}
    		try {
    			if (out != null) {
    				out.close();
    			}
    			if (in != null) {
    				in.close();
    			}
    		} catch (final IOException e) {
    			e.printStackTrace();
    		}
    	}
    	return false;
    }
    其中hashKeyFromUrl这个函数用来将网络上图片的Url装换为key,因为网络上的Url可能包含不合法字符,这个在前面的源码分析中已经讲解过。

    然后通过mDiskLruCache.edit(key);通过key构造一个Editor对象,然后editor.newOutputStream(DISK_CACHE_INDEX)获取文件输出流(DISK_CACHE_INDEX通常指定为0),然后将该输出流和网络上图片的Url作为参数传递给downloadUrlToStream(String urlString, OutputStream outputStream) 函数,该函数的作用是通过制定的图片的Url和OutputStream 将网络上的图片通过outputStream写入到本地文件中,这里传入的是DiskLruCache的输出流,所以就将其写入到了磁盘缓存中。注意该操作要在一个子线程中进行,下载完成之后还用调用editor的commit方法才能将其真真正正写入缓存。如果下载过程出现错误,则会通过Editor的abort()函数来回退整个操作。

    获取缓存:获取缓存主要是通过public synchronized Snapshot get(String key) 函数来完成的。代码如下:

    try {
    	String imageUrl = "http://www.baidu.com/logo.jpg";
    	String key = hashKeyFromUrl(imageUrl);
    	DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
           
    	if (snapShot != null) {
    		FileInputStream fis =(FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
    		Bitmap bitmap = BitmapFactory.decodeStream(fis);//注意此种方式未对网络上获取的图片进行压缩处理
    		mImage.setImageBitmap(bitmap);
    	}
    } catch (IOException e) {
    	e.printStackTrace();
    }
    即调用mDiskLruCache.get(key);获取DiskLruCache.Snapshot对象,通过该对象的snapShot.getInputStream(DISK_CACHE_INDEX);获取输入流,获取到该输入流后就基本上和本地文件操作是类似的,很容易将其转换为一个Bitmap对象,注意上述代码中未对获取的图片进行压缩处理,直接显示在ImageView控件上这是不妥的,关于图片压缩的内容请参看我的博客:安卓图片压缩技术


    好了,以上就是本人理解的关于DiskLruCache相关的知识点,看官如果觉得不错,请记得点击下方的“顶”或“赞”按钮给我一点小小的鼓励哦微笑,看官也可以看看我的其它博客的文章哦!微笑






    展开全文
  • 浏览器缓存机制介绍与缓存策略剖析.pptx
  • 常见的缓存策略的优劣势对比发布时间:2020-04-07 16:02:31来源:亿速云阅读:65作者:小新今天小编给大家分享的是常见的缓存策略的优劣势对比,很多人都不太了解,今天小编为了让大家更加了解缓存策略的优劣势,...

    常见的缓存策略的优劣势对比

    发布时间:2020-04-07 16:02:31

    来源:亿速云

    阅读:65

    作者:小新

    今天小编给大家分享的是常见的缓存策略的优劣势对比,很多人都不太了解,今天小编为了让大家更加了解缓存策略的优劣势,所以给大家总结了以下内容,一起往下看吧。一定会有所收获的哦。

    众所周知,想要提高系统的性能,缓存是最直接也是最简单的方法之一。缓存一方面可以减少数据库负载,另一方面还可以减少相应时间并且节省成本。今天,小编将向大家介绍几种比较常见的缓存策略,即Cache-Aside、Read-Though Cache、Write-Through Cache、Write-Around和Write-Back。下面让我们一起对比分析一下常用缓存策略的优劣以及使用场景吧!

    af996e8ac820b05756cf393bf89dede0.png

    缓存策略一:Cache-Aside

    Cache-Aside可以说时最常用的缓存策略。缓存位于一边,应用程序直接与缓存和数据库对话。应用程序首先检查缓存;如果在缓存中找到,表示已经命中缓存。数据被读取并返回给应用程序;如果在缓存中没有找到,则未命中缓存。应用程序必须做一些额外的工作,它需要查询数据库来读取数据,将数据返回给客户端,然后还要将数据存储在缓存中,这样对相同数据的后续读取可以命中缓存。该项策略适合读多的应用场景。使用Cache-aside的系统对缓存失效具有一定的弹性。如果缓存集群宕机,系统仍然可以通过直接访问数据库进行操作。另外,缓存中的数据模型可以与数据库中的数据模型不同。

    缓存策略二:Read-Though Cache

    Read-though策略下的缓存与数据库保持一致。当缓存丢失时,它从数据库加载相应的数据,填充缓存并将其返回给应用程序。cache-aside和read-through策略都是延迟加载数据的,也就是说,只在第一次读取数据时才加载数据。对比read-through和cache-aside,它们的最大区别在于,在cache-aside中应用程序负责从数据库中获取数据并填充缓存。在read-through中,此逻辑通常由库或独立缓存提供程序支持,而且read-through cache中的数据模型不能与数据库中的数据模型不同。总的来讲,read-through缓存最适合于读量较大的工作负载。劣势是,当第一次请求数据时,它总是导致缓存丢失,并导致额外的数据加载到缓存的代价。

    缓存策略三:Write-Through Cache

    在这种缓存策略中,首先将数据写入缓存,然后写入数据库。缓存与数据库保持一致,写操作总是通过缓存到达主数据库。就其本身而言,write-through缓存似乎没有多大作用,实际上,它们引入了额外的写延迟,因为数据先写到缓存,然后写到主数据库。但是,当与read-through结合使用时,我们获得了read-through的所有好处,还获得了数据一致性保证,使我们不必使用缓存失效技术。DynamoDB Accelerator (DAX)是write-through / read-through cache的一个很好的例子。它与DynamoDB和应用程序内联。对DynamoDB的读写可以通过DAX完成。

    0e36f5f6db48243f0908aba9ec2e106d.png

    缓存策略四:Write-Around

    这种缓存策略下,数据直接写入数据库,只有读取的数据才能进入缓存。Write-around可以与read-through结合使用,并在数据只写一次、读取次数较少或从不读的情况下提供良好的性能。例如,实时日志或聊天室消息。同样,这个模式也可以与cache-aside组合使用。

    缓存策略五:Write-Back

    这种缓存策略下,应用程序将数据写入缓存,缓存会立即确认,并在延迟一段时间后将数据写入数据库。有时这种策略也被称为write-behind。Write-back缓存提高了写性能,对于写工作量大的工作负载非常有用。当与read-through相结合的时候,它对于混合工作负载非常有效,最近更新和访问的数据总是在缓存中可用。它对数据库故障具有很大程度上的弹性,可以容忍一些数据库的宕机。如果支持批处理或合并,则可以减少对数据库的总体写操作,这将减少负载并降低成本。一些开发人员使用cache-aside和write-back两种策略,以便更好地吸收峰值负载期间的峰值。这种缓存策略的优劣十分明显,它主要劣势是,如果缓存失效,数据可能会永久丢失。大多数关系数据库存储引擎的内部都默认启用了回写缓存。查询首先写入内存,最后刷新到磁盘。

    以上就是常见的缓存策略的优劣势对比的简略介绍,当然详细使用上面的不同还得要大家自己使用过才领会。如果想了解更多,欢迎关注亿速云行业资讯频道哦!

    展开全文
  • http缓存策略

    2021-05-23 14:21:19
    协商缓存 让客户端与服务器之间能实现缓存文件是否更新...服务器通知浏览器一个缓存时间,在缓存时间内,下次请求,直接用缓存,不在时间内,执行比较缓存策略 即协商缓存可以提供是否更新的验证,不是直接使用 ...
  • P2P缓存系统中总开销最小的协作缓存策略
  • CDN缓存策略

    2020-11-12 18:05:10
    CDN缓存策略 CDN边缘节点缓存策略因服务商不同而不同,但一般都会遵循http标准协议,通过http响应头中的Cache-control: max-age的字段来设置CDN边缘节点数据缓存时间。 当客户端向CDN节点请求数据时,CDN节点会判断...
  • mybatis缓存策略

    2019-03-21 17:26:50
    像大多数的持久化框架一样,mybatis也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis中分为一级缓存和二级缓存。 一级缓存是基于SQLSession的缓存,一级缓存的内容不能跨...
  • 文章目录Hutool - 缓存相关,缓存策略以及示例代码1、缓存策略2、创建一个缓存3、先入先出-FIFOCache4、最少使用-LFUCache5、最近最久未使用-LRUCache6、过期策略缓存 TimedCache Hutool - 缓存相关,缓存策略以及...
  • WEB缓存策略

    2018-03-16 10:22:09
    WEB缓存策略 在计算机领域,缓存是一个常见的词汇,其核心目的是为了追求更快的速度,提高系统的整体性能,本文总结一下自己对WEB缓存策略理解。 WEB缓存策略,目的是通过让client与server之间尽可能少的传输重复...
  • 浏览器缓存策略 浏览器缓存策略 Httpwatch Httpwatch
  • 一、Android中的缓存策略 一般来说,缓存策略主要包含缓存的添加、获取和删除这三类操作。如何添加和获取缓存这个比较好理解,那么为什么还要删除缓存呢?这是因为不管是内存缓存还是硬盘缓存,它们的缓存大小都是...
  • Gradle 缓存目录结构 缓存策略

    千次阅读 2019-07-09 11:04:04
    gradle 缓存策略 Gradle 的缓存策略中,对于 SNAPSHOT 版本默认的缓存周期是 24 小时,也就是从我上次更新之后,24小时内都会使用上次的缓存 Gradle 对于动态版本和变化模块的缓存时间默认是 24 小时。 动态版本 ...
  • P2P节点缓存策略

    2013-12-03 09:44:00
    P2P节点缓存策略
  • 浏览器缓存策略

    千次阅读 2019-05-26 02:41:48
    1.前言什么是浏览器缓存?浏览器缓存,是当你从...一图胜千言,下面通过图来说明缓存的过程,基于这个过程,会行使不同的缓存策略。从这个图可以知道:当我们第一次请求服务器的时候,服务器便会返回资源并决定...
  • megacli 缓存策略

    2019-10-25 10:49:26
    缓存策略 是指raid卡在写入磁盘之前如何使用板载ram来收集数据,或者在系统请求之前读取数据。 当我们有大量的数据要写时,使用写缓存,它可以更快地将数据顺序写入磁盘,而不是写入小块数据。 当系统请求某些数据...
  • 上一篇咱们讲了ImageLoader的缓存策略,那么Glide的缓存策略相较于ImageLoader缓存有什么优势吗?它们之间有什么样的差别,或者说性能方面,那一个更好,注意咱们这里所介绍的缓存是不包括sd卡存储的。先来看一看...
  • 缓存策略缓存策略主要三方面: 缓存什么内容 何时进行缓存 当缓存空间已满时如何进行替换,即缓存替换算法。 对于第二方面,大部分缓存算法使用预取策略来提前将部分磁盘数据放入缓存,...
  • MyBatis缓存策略之二级缓存
  • 前端本地缓存概况之浏览器缓存策略

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,498
精华内容 6,199
关键字:

缓存策略