国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

重拾Java Network Programming(四)URLConnection & C

Guakin_Huang / 1140人閱讀

摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。

前言

本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于URL的緩存。本文將涉及如下內(nèi)容:

HTTP協(xié)議

HTTP協(xié)議中與緩存相關(guān)的內(nèi)容

URLConnection 和 HTTPURLConnection

ResponseCache,CacheRequest,CacheResponse

WHAT & WHY

正常來(lái)說(shuō),服務(wù)器和客戶端的HTTP通信需要首先通過(guò)TCP的三次握手建立連接,然后客戶端再發(fā)出HTTP請(qǐng)求并接收服務(wù)器的響應(yīng)。但是,在有些時(shí)候,服務(wù)器的資源并沒(méi)有發(fā)生改變。此時(shí)重復(fù)的向服務(wù)器請(qǐng)求同樣的資源會(huì)帶來(lái)帶寬的浪費(fèi)。針對(duì)這種情況我們可以采用緩存的方式,既可以是本地緩存,也可以是代理服務(wù)器的緩存,來(lái)減少對(duì)服務(wù)器資源的不必要的訪問(wèn)。從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。

那么我們?nèi)绾沃?,何時(shí)可以直接使用緩存,何時(shí)因?yàn)楫?dāng)前的緩存已經(jīng)過(guò)時(shí),需要重新向資源所在的服務(wù)器發(fā)出請(qǐng)求呢?

緩存關(guān)鍵字

HTTP1.0和HTTP1.1分別針對(duì)緩存提供了一些HEADER屬性供連接雙方參考。需要注意,如果是HTTP1.0的服務(wù)器,將無(wú)法識(shí)別HTTP1.1的緩存屬性。所以有時(shí)候?yàn)榱讼蛳录嫒菪裕覀儠?huì)設(shè)置多個(gè)和緩存相關(guān)的屬性。當(dāng)然,它們彼此之間是存在優(yōu)先級(jí)的,后面將會(huì)詳細(xì)介紹。

Expires

支持HTTP1.0,說(shuō)明該資源在Expires內(nèi)容之后過(guò)期。Expires關(guān)鍵字使用的是絕對(duì)日期。

Cache-control

支持HTTP1.1,使用相對(duì)日期對(duì)緩存進(jìn)行管理。它可定義的屬性包括:
max-age=[seconds]: 當(dāng)前時(shí)間經(jīng)過(guò)n秒后緩存資源失效
s-maxage=[seconds]: 從共享緩存獲取的數(shù)據(jù)在n秒后失效,私有緩存往往可以更久一些
public: 表明響應(yīng)可以被任何對(duì)象(包括:發(fā)送請(qǐng)求的客戶端,代理服務(wù)器,等等)緩存。
private: 表明響應(yīng)只能被單個(gè)用戶緩存,不能作為共享緩存(即代理服務(wù)器不能緩存它)。
no-cache: 允許緩存,但每次訪問(wèn)緩存時(shí)必須重新驗(yàn)證緩存的有效性
no-store: 緩存不應(yīng)存儲(chǔ)有關(guān)客戶端請(qǐng)求或服務(wù)器響應(yīng)的任何內(nèi)容。
must-revalidate: 緩存必須在使用之前驗(yàn)證舊資源的狀態(tài),并且不可使用過(guò)期資源。
還有許多相關(guān)的屬性,想要詳細(xì)了解的話可以參考這篇文章。

If-Modified—Since/If-Unmodified-Since

僅僅是已緩存文檔的過(guò)期并不意味這它和原始服務(wù)器上目前處于活躍狀態(tài)的資源有實(shí)際的區(qū)別,只是意味著到了要核實(shí)的時(shí)間。這種情況稱為服務(wù)器再驗(yàn)證。

if-modified-since:說(shuō)明在date之后文檔被修改了的話,就執(zhí)行請(qǐng)求的方法,即條件式的再驗(yàn)證。通常和服務(wù)器的last-modified響應(yīng)頭部配合使用。last-modified說(shuō)明該資源最后一次的修改時(shí)間。如果資源的這個(gè)屬性發(fā)生變化,則說(shuō)明緩存已經(jīng)失效。則服務(wù)器會(huì)返回最新的資源。否則會(huì)返回304 not modified響應(yīng)。

這種方式的好處在于,如果資源未失效,則無(wú)需重傳資源,可以有效的節(jié)省帶寬。

與之相類似的有if-unmodified-since,該屬性的意思是如果資源在該日期之后被修改了,則不執(zhí)行請(qǐng)求方法。通常在進(jìn)行部分文件傳輸時(shí),獲取文件的其余部分之前要確保文件未發(fā)生變化,此時(shí)這個(gè)首部很有用。

If-None-Match/If-Match/If-Range

有些時(shí)候,僅僅是使用最后修改日期再驗(yàn)證是不夠的:

有些文檔可能被周期性重寫(xiě),但是實(shí)際的數(shù)據(jù)常常是一樣的。也就是說(shuō)內(nèi)容沒(méi)有變化,但是修改日期變化了。

有些文檔可能被修改了,但是所做的修改并不重要,不需要所有的緩存都重裝數(shù)據(jù)。

有些服務(wù)器無(wú)法準(zhǔn)確的判定最后的修改日期

有些文檔會(huì)在更小的時(shí)間粒度發(fā)生變化(比如監(jiān)視器,股票等),此時(shí)以秒為最小單位的修改日期可能不夠用

為此,HTTP提供了實(shí)體標(biāo)簽(ETag)的比較。當(dāng)發(fā)布者對(duì)文檔進(jìn)行修改時(shí),可以修改文檔的實(shí)體標(biāo)簽來(lái)說(shuō)明新的版本。這樣,只要實(shí)體標(biāo)簽改變,緩存就可以用If-None-Match條件首部來(lái)獲取新的副本。

服務(wù)器在響應(yīng)中會(huì)標(biāo)記當(dāng)前資源的ETag。一旦文檔過(guò)期后,可以使用HEAD請(qǐng)求來(lái)?xiàng)l件式再驗(yàn)證。如果服務(wù)器上ETag改變,則會(huì)返回最新的資源。當(dāng)然,ETag可以包含多個(gè)內(nèi)容,說(shuō)明本地存儲(chǔ)了多個(gè)版本的副本。如果沒(méi)有命中這些副本,再返回完整資源。

If-None-Match: "v2.4","v2.5","v2.6"

如果服務(wù)器收到的請(qǐng)求中既帶有if-modified-since,又帶有實(shí)體標(biāo)簽條件首部,那么只有這兩個(gè)條件都滿足時(shí),才會(huì)返回304 not modified響應(yīng)。

Cache in JAVA

默認(rèn)情況下。JAVA不緩存任何任何內(nèi)容。我們需要通過(guò)自己的實(shí)現(xiàn)來(lái)支持URL的緩存。我們需要實(shí)現(xiàn)以下抽象類:

ResponseCache

CacheRequest

CacheResponse

這里其實(shí)使用的是Template Pattern。有興趣的話可以去了解一下。

ResponseCache 需要實(shí)現(xiàn)的方法

    //根據(jù)URI,請(qǐng)求的方法以及請(qǐng)求頭獲取緩存的響應(yīng)。如果響應(yīng)過(guò)期,則重新發(fā)出請(qǐng)求
    public abstract CacheResponse get(URI uri, String rqstMethod, Map> rqstHeaders) throws IOException; 

    //在獲取到響應(yīng)之后調(diào)用該方法
    //如果該響應(yīng)不可以被緩存,則返回null
    //如果該響應(yīng)可以被緩存,則返回CacheRequest對(duì)象,可以利用其下的OutputStream來(lái)寫(xiě)入緩存的內(nèi)容
    public CacheRequest put(URI uri, URLConnection conn) throws IOException; 

CacheRequest需要實(shí)現(xiàn)的方法:

    //獲取寫(xiě)入緩存的輸入流
    @Override
    public OutputStream getBody() throws IOException;
    
    //放棄當(dāng)前的緩存
    @Override
    public void abort();

CacheResponse需要實(shí)現(xiàn)的方法

    //獲取響應(yīng)頭
    @Override
    public Map> getHeaders() throws IOException; 

    //獲取響應(yīng)體的輸入流,即從InputStream中即可讀取緩存的內(nèi)容
    @Override
    public InputStream getBody() throws IOException; 

這里的流程基本如下:
當(dāng)啟動(dòng)URLConnection連接時(shí),URLConnection會(huì)先訪問(wèn)ResponseCache的get方法,詢問(wèn)緩存是否命中想要的數(shù)據(jù)。輸入的參數(shù)包括URI,請(qǐng)求方法(通常指緩存GET請(qǐng)求),以及請(qǐng)求頭(如果請(qǐng)求頭中明確要求不訪問(wèn)緩存,則直接返回null)。如果命中,則返回CacheResponse對(duì)象,從該對(duì)象中獲取緩存的輸入流。 如果沒(méi)有命中,則會(huì)啟動(dòng)連接,并將獲取的數(shù)據(jù)使用ResponseCache的put方法寫(xiě)入緩存。該方法會(huì)返回一個(gè)輸出流用于存儲(chǔ)緩存。

Cache Implementation In JAVA

現(xiàn)在我需要實(shí)現(xiàn)緩存,我將會(huì)在put時(shí)判斷該資源是否允許緩存(通常有cache-control參數(shù)來(lái)提供)。我也會(huì)在get時(shí)判讀能否從緩存中命中資源以及該資源是否失效,如果失效就從緩存中刪除,否則直接返回,無(wú)需訪問(wèn)服務(wù)器。這里我還通過(guò)一個(gè)后臺(tái)線程遍歷緩存數(shù)據(jù)結(jié)構(gòu),及時(shí)將失效的資源從緩存中刪除。

MyCacheRequest使用ByteArrayOutputStream將緩存內(nèi)容通過(guò)內(nèi)存IO存儲(chǔ)在內(nèi)存中

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.CacheRequest;

public class MyCacheRequest extends CacheRequest{
    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    public MyCacheRequest(){

    }
    @Override
    public OutputStream getBody() throws IOException {
        return outputStream;
    }

    @Override
    public void abort() {
        outputStream.reset();
    }

    public byte[] getData(){
        if (outputStream.size() == 0) return null; else return outputStream.toByteArray();
    }
}

MyCacheResponse存儲(chǔ)了請(qǐng)求頭,并將cache-control的信息封裝在了CacheControl類中:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.CacheResponse;
import java.net.URLConnection;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class MyCacheResponse extends CacheResponse {
    private final MyCacheRequest cacheRequest;
    private final Map> headers;
    private final Date expires;
    private final CacheControl control;

    public MyCacheResponse(MyCacheRequest cacheRequest, URLConnection uc, CacheControl control){
        this.cacheRequest = cacheRequest;
        this.headers = uc.getHeaderFields();
        this.expires = new Date(uc.getExpiration());
        this.control = control;
    }
    @Override
    public Map> getHeaders() throws IOException {
        return this.headers;
    }

    @Override
    public InputStream getBody() throws IOException {
        return new ByteArrayInputStream(cacheRequest.getData());
    }

    public boolean isExpired() {
        Date now = new Date();
        if (control.getMaxAge() !=null && control.getMaxAge().before(now)) return true;
        else if (expires != null) {
            return expires.before(now);
        } else {
            return false;
        }
    }

    public CacheControl getControl() {
        return control;
    }
}

CacheControl類如下這里只用到了基本的max-age屬性和no-store屬性

import java.util.Date;
import java.util.Locale;

/**
 * 封裝HTTP協(xié)議中cache—control對(duì)應(yīng)的屬性
 */
public class CacheControl {

    private Date maxAge;
    private Date sMaxAge;
    private boolean mustRevalidate;
    private boolean noCache;
    private boolean noStore;
    private boolean proxyRevalidate;
    private boolean publicCache;
    private boolean privateCache;

    private static final String MAX_AGE = "max-age=";
    private static final String SMAX_AGE = "s-maxage=";
    private static final String MUST_REVALIDATE = "must-revalidate";
    private static final String PROXY_REVALIDATE = "proxy-revalidate";
    private static final String NO_CACHE = "no-cache";
    private static final String NO_STORE = "no-store";
    private static final String PUBLIC_CACHE = "public";
    private static final String PRIVATE_CACHE = "private";


    public CacheControl(String s){
        if (s == null || s.trim().isEmpty()) {
            return; // default policy
        }

        String[] components = s.split(",");

        Date now = new Date();

        for (String component : components){
            try {
                component = component.trim().toLowerCase(Locale.US);

                if (component.startsWith(MAX_AGE)){
                    int secondsInTheFuture = Integer.parseInt(component.substring(MAX_AGE.length()));
                    maxAge = new Date(now.getTime() + 1000 * secondsInTheFuture);
                }else if (component.startsWith(SMAX_AGE)){
                    int secondsInTheFuture = Integer.parseInt(component.substring(SMAX_AGE.length()));
                    sMaxAge = new Date(now.getTime() + 1000 * secondsInTheFuture);
                }else if (component.equals(MUST_REVALIDATE)){
                    mustRevalidate = true;
                }else if (component.equals(PROXY_REVALIDATE)){
                    proxyRevalidate = true;
                }else if (component.equals(NO_CACHE)){
                    noCache = true;
                }else if (component.equals(NO_STORE)){
                    noStore = true;
                }else if (component.equals(PUBLIC_CACHE)){
                    publicCache = true;
                }else if (component.equals(PRIVATE_CACHE)){
                    privateCache = true;
                }
            }catch (RuntimeException ex) {
                continue; }
        }
    }

    public Date getMaxAge() {
        return maxAge;
    }

    public Date getsMaxAge() {
        return sMaxAge;
    }

    public boolean isMustRevalidate() {
        return mustRevalidate;
    }

    public boolean isNoCache() {
        return noCache;
    }

    public boolean isNoStore() {
        return noStore;
    }

    public boolean isProxyRevalidate() {
        return proxyRevalidate;
    }

    public boolean isPublicCache() {
        return publicCache;
    }

    public boolean isPrivateCache() {
        return privateCache;
    }
}

ResponseCache類使用ConcurrentHashMap進(jìn)行緩存的同步讀寫(xiě)。這里默認(rèn)緩存達(dá)到上限就不再存入新的緩存。建議可以通過(guò)隊(duì)列或是LinkedHashMap實(shí)現(xiàn)FIFO或是LRU管理。

import java.io.IOException;
import java.net.*;
import java.util.List;
import java.util.Map;

public class MyResponseCache extends ResponseCache{
    private final Map responses;
    private final int SIZE;

    public MyResponseCache(Map responses, int size){
        this.responses = responses;
        this.SIZE = size;

    }
    /**
     *
     * @param uri 路徑 - equals方法將不會(huì)調(diào)用DNS服務(wù)
     * @param rqstMethod - 請(qǐng)求方法 一般只緩存GET方法
     * @param rqstHeaders - 判斷是否可以緩存
     * @return
     * @throws IOException
     */
    @Override
    public CacheResponse get(URI uri, String rqstMethod, Map> rqstHeaders) throws IOException {
        if ("GET".equals(rqstMethod)) {

            MyCacheResponse response = responses.get(uri); // check expiration date
            if (response != null && response.isExpired()) {
                responses.remove(uri);
                response = null;
            }
            return response;
        }
        return null;
    }

    @Override
    public CacheRequest put(URI uri, URLConnection conn) throws IOException {
        if (responses.size() >= SIZE) return null;
        CacheControl cacheControl = new CacheControl(conn.getHeaderField("Cache-Control"));

        if (cacheControl.isNoStore()){
            System.out.println(conn.getHeaderField(0));
            return null;
        }

        MyCacheRequest myCacheRequest = new MyCacheRequest();
        MyCacheResponse myCacheResponse = new MyCacheResponse(myCacheRequest, conn ,cacheControl);
        responses.put(uri, myCacheResponse);
        return myCacheRequest;
    }
}

CacheValidator后臺(tái)任務(wù),將失效的緩存刪除:

import java.net.URI;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CacheValidator implements Runnable{
    boolean stop;

    private ConcurrentHashMap map;

    public CacheValidator(ConcurrentHashMap map){
        this.map = map;
    }
    @Override
    public void run() {
        while (!stop){
            for (Map.Entry entry : map.entrySet()){
                if (entry.getValue().isExpired()){
                    System.out.println(entry.getKey());
                    map.remove(entry.getKey());
                }
            }
        }
    }
}

最后使用主線程啟動(dòng)緩存,注意這里需要顯式的設(shè)置緩存器和開(kāi)啟URLConnection的緩存。默認(rèn)情況下,JAVA不開(kāi)啟緩存。同時(shí)JAVA全局只支持一個(gè)緩存的存在。

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap map = new ConcurrentHashMap<>();
        MyResponseCache myResponseCache = new MyResponseCache(map, 20);
        //設(shè)置默認(rèn)緩存器
        ResponseCache.setDefault(myResponseCache);

        //設(shè)置后臺(tái)線程
        Thread thread = new Thread(new CacheValidator(map));
        thread.setDaemon(true);
        thread.start();

        System.out.println(map.size());
        fetchURL(SOME_URL);

        TimeUnit.SECONDS.sleep(20000);


    }

    public static void fetchURL(String location){
        try {
            URL url = new URL(location);
            URLConnection uc = url.openConnection();
            //開(kāi)啟緩存
            uc.setDefaultUseCaches(true);

            BufferedInputStream bfr = new BufferedInputStream(uc.getInputStream());
            int c;
            while ((c = bfr.read()) != -1){
//                System.out.print((char) c);
                //do something
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}


想要了解更多開(kāi)發(fā)技術(shù),面試教程以及互聯(lián)網(wǎng)公司內(nèi)推,歡迎關(guān)注我的微信公眾號(hào)!將會(huì)不定期的發(fā)放福利哦~

參考書(shū)籍
HTTP 權(quán)威指南
Java Network Programming

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/71037.html

相關(guān)文章

  • 重拾Java Network ProgrammingURLConnection &amp; C

    摘要:從而一方面減少了響應(yīng)時(shí)間,另一方面減少了服務(wù)器的壓力。表明響應(yīng)只能被單個(gè)用戶緩存,不能作為共享緩存即代理服務(wù)器不能緩存它。這種情況稱為服務(wù)器再驗(yàn)證。否則會(huì)返回響應(yīng)。 前言 本文將根據(jù)最近所學(xué)的Java網(wǎng)絡(luò)編程實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于URL的緩存。本文將涉及如下內(nèi)容: HTTP協(xié)議 HTTP協(xié)議中與緩存相關(guān)的內(nèi)容 URLConnection 和 HTTPURLConnection Respo...

    魏明 評(píng)論0 收藏0
  • 重拾Java Network Programming(一)IO流

    摘要:不同類型的流入,往往對(duì)應(yīng)于不同類型的流數(shù)據(jù)。所以通常會(huì)將字節(jié)緩存到一定數(shù)量后再發(fā)送。如果是,則將兩個(gè)標(biāo)記都拋棄并且將之前的內(nèi)容作為一行返回。因此二者陷入死鎖。因此推出了和類。 前言 最近在重拾Java網(wǎng)絡(luò)編程,想要了解一些JAVA語(yǔ)言基本的實(shí)現(xiàn),這里記錄一下學(xué)習(xí)的過(guò)程。 閱讀之前,你需要知道 網(wǎng)絡(luò)節(jié)點(diǎn)(node):位于網(wǎng)絡(luò)上的互相連通的設(shè)備,通常為計(jì)算機(jī),也可以是打印機(jī),網(wǎng)橋,路由器等...

    Lycheeee 評(píng)論0 收藏0
  • 重拾Java Network Programming(二)InetAddress

    摘要:前言今天,我將梳理在網(wǎng)絡(luò)編程中很重要的一個(gè)類以及其相關(guān)的類。這類主機(jī)通常不需要外部互聯(lián)網(wǎng)服務(wù),僅有主機(jī)間相互通訊的需求??梢酝ㄟ^(guò)該接口獲取所有本地地址,并根據(jù)這些地址創(chuàng)建。在這里我們使用阻塞隊(duì)列實(shí)現(xiàn)主線程和打印線程之間的通信。 前言 今天,我將梳理在Java網(wǎng)絡(luò)編程中很重要的一個(gè)類InetAddress以及其相關(guān)的類NetworkInterface。在這篇文章中將會(huì)涉及: InetA...

    daryl 評(píng)論0 收藏0
  • Glide的源碼分析(二) 2.2

    摘要:從網(wǎng)絡(luò)加載圖片加載從加載從網(wǎng)絡(luò)加載從加載具體的方法實(shí)現(xiàn)接口的類以后再做分析,而從網(wǎng)絡(luò)加載兩步從網(wǎng)絡(luò)獲取數(shù)據(jù)處理數(shù)據(jù)。 4.從網(wǎng)絡(luò)加載 EngineJob current = jobs.get(key); if (current != null) { current.addCallback(cb); if (...

    warkiz 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<