精华内容
下载资源
问答
  • keep

    2021-06-23 12:37:42
    中文名keep短语keep a seat注意keep还是程序里的一个常用函数释义为保持keep英文释义编辑语音用作及物动词(1)保留、保存、保持、留下e.g. We'd better keep a seat for him.我们最好给他留个座位。H...

    本词条缺少概述图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧!

    keep是一个常用英语单词,既可以做及物动词也可以做不及物动词。

    中文名

    keep

    短    语

    keep a seat注    意

    keep还是程序里的一个常用函数

    释义为

    保持

    keep英文释义

    编辑

    语音

    用作及物动词(1)保留、保存、保持、留下

    e.g. We'd better keep a seat for him.

    我们最好给他留个座位。

    He kept all the money in the bank.

    他把所有的钱都存入了银行。

    (2)履行(诺言)遵守

    e.g. One should keep one's promise.

    一个人应当遵守自己的诺言。

    Everybody must keep the law.

    人人都必须守法。

    (3)赡养,养活,饲养

    e.g. He has a large family to keep.

    他有一大家人要养活。

    The old man kept many animals like dogs, pigs and cats.

    这位老人养了许多动物,像狗、猪、还有猫等。

    (4)经营,管理

    e.g. He kept a hotel in this city.

    在这座城市里,他开了一家旅店。

    She is good at keeping house.

    她擅长管理家务。

    (5)保守(秘密),记(日记、帐)

    Mary keeps a diary every day

    玛丽天天记日记。

    You must keep secrets for your friends.

    你必须为你的朋友们保守秘密

    (6)庆祝;守(宗教节日等)

    e.g. All of the people keep the Spring Festival in our country.

    我国所有的人都庆祝春节。

    Some of them keep birthdays.

    他们中有些人庆祝生日。

    (7)使……处于某种状态(情况)

    在这种情况下,keep常跟复合结构(keep+宾语+补语)。用作宾语补足语常见的词有现在分词、过去分词、形容词、副词以及介词短语。

    e.g. He kept me waiting for half an hour.

    他让我等了半个小时。

    Keep your mouth shut and your eyes open.

    少说话,多观察。

    The doctor kept me in for a week.

    医生一周没让我出去。

    He always keeps his books in good order.

    他总是把书放得整整齐齐。

    .keep +形容词

    keep+sth/sb +形容词

    keep +doing 一直做某事.keep ...from doing ...阻止做某事.

    keep a pet 饲养一个宠物

    How long may I keep this book keep指借.

    用作不及物动词

    (1)保持、继续(处于某种状态)(keep为连系动词)

    e.g. Please keep quiet.

    请保持安静。

    We're keeping in very good health.

    我们身体非常好。

    (2)(食物)保持良好状态

    e.g. Will this fish keep till tomorrow?

    这鱼能放到明天吗?

    C、keep构成的一些短语

    keep (sb.) away (from sth.)(使)某人离开(某物)

    keep sb. from doing sth. 阻止某人做某事

    keep sth. in mind 记住(某事物)

    keep sb./ sth. out (of sth.) 不让……入内

    keep back 忍住(眼泪),扣下,隐瞒

    keep in touch with 与……保持联系

    keep (on) doing sth. 继续做某事

    keep off 远离,避开,让开

    keep on 穿戴着。。。;使(灯等)一直开的。

    keep out 使留在外面

    keep up 保持(不低落),继续

    keep up with 跟上,不落在后面

    both and既...且...;...和...都

    both of两者(都);两个(都);双方(都)

    neither nor既不...也不;也不

    neither of(两者之中)无一个

    either or或者

    either of (两者之中)任何一个

    keep单词要点

    编辑

    语音

    1.keep的基本意思是“保留,保管,保存,留下,保持”,指使某人或某物继续保持某种状态。引申可作“管理,经营”“料理,照顾”“抚养,养活,饲养”“遵守,维护”“庆祝”“耽搁”“记”等解。

    2.keep既可用作及物动词,也可用作不及物动词。用作及物动词时,接名词或代词作宾语(不可接动词不定式作宾语),也可接双宾语,其间接宾语可转化为介词for的宾语。keep用作不及物动词作“保存”解时,主动结构常含被动意义。

    3.keep还可接以形容词、副词、as短语、现在分词(短语)、过去分词(短语)等充当宾语补足语的复合宾语,但不可接含动词不定式的复合宾语。

    4.keep作“保持,继续”解时还可用作系动词,后跟表语,该表语可由形容词、副词、名词、动名词或介词短语等充当,此用法keep有时可用于进行体。

    5.keep后接介词from,再接名词或动名词,表示“使自己不做某事”,主要用于否定句或疑问句。

    6.由keep构成的常用习语大多不用于进行体。

    keep计算机函数

    编辑

    语音

    函数名: keep

    功 能: 退出并继续驻留

    用 法: void keep(int status, int size);

    程序例:

    /***NOTE:

    This is an interrupt service routine. You

    can NOT compile this program with Test

    Stack Overflow turned on and get an

    executable file which will operate

    correctly. Due to the nature of this

    function the formula used to compute

    the number of paragraphs may not

    necessarily work in all cases. Use with

    care! Terminate Stay Resident (TSR)

    programs are complex and no other support

    for them is provided. Refer to the

    MS-DOStechnical documentation

    for more information. */

    #include

    /* The clock tick interrupt */

    #define INTR 0x1C

    /* Screen attribute (blue on grey) */

    #define ATTR 0x7900

    /* reduce heaplength and stacklength

    to make a smaller program in memory */

    extern unsigned _heaplen = 1024;

    extern unsigned _stklen = 512;

    void interrupt ( *oldhandler)(void);

    void interrupt handler(void)

    {

    unsigned int (far *screen)[80];

    static int count;

    /* For a color screen the video memory

    is at B800:0000. For a monochrome

    system use B000:000 */

    screen =MK_FP(0xB800,0);

    /* increase the counter and keep it

    within 0 to 9 */

    count++;

    count %= 10;

    /* put the number on the screen */

    screen[0][79] = count + '0' + ATTR;

    /* call the old interrupt handler */

    oldhandler();

    }

    int main(void)

    {

    /* get the address of the current clock

    tick interrupt */

    oldhandler = getvect(INTR);

    /* install the new interrupt handler */

    setvect(INTR, handler);

    /* _psp is the starting address of the

    program in memory. The top of the stack

    is the end of the program. Using _SS and

    _SP together we can get the end of the

    stack. You may want to allow a bit of

    saftey space to insure that enough room

    is being allocated ie:

    (_SS + ((_SP + safety space)/16) - _psp)

    */

    keep(0, (_SS + (_SP/16) - _psp));

    return 0;

    }

    keep智能报警器

    编辑

    语音

    d52186a5171a8788ae584f7e72415a06.png防丢、防忘、防遗漏、防水的Keep。Keep是采用最新蓝牙4.0版本高效低功耗无线连接技术,创造性的开发出防丢与遥控功能于一体的多功能产品。

    设备与具备蓝牙4.0功能的主流智能手机成功连接后,用户手机与Keep超出设置连接距离时,手机开始震动和响铃提醒用户,且蓝牙防丢器开始启动报警声及闪光提示,以便用户发现和查找位置。除此之外Keep在与手机保持连接的状态下,还具备遥控手机拍照、录音、录影、寻找汽车、手机照明等功能。参考资料

    1.

    防丢、防忘、防遗漏、防水蓝牙4.0智能遥控报警器Keep

    .爱搞机.2013-03-31[引用日期2013-04-01]

    展开全文
  • 了解到他们使用了keep-alive,之前对于keep-alive的实现仅在NIO模式下有跟踪过,对于BIO和APR模式下如何实现的keep-alive没有很深入的了解,正好借这次问题排查详细的跟踪了一下另外两种模式下对keep-alive的实现。...

    Tomcat的connector实现逻辑蛮复杂的,有很多种状态总记不住,每次遇到网络相关的问题都要翻一遍代码,这次结合一个案例看看tomcat的三种connector的实现方式。

    这个案例在毕玄的blog里也提到了,背景是某应用上游有个用c写的模块与server端tomcat进行http通讯,这个应用tomcat配置的connector是apr模式。之前一直运行的很稳定,但一次前端扩容后,导致后端的tomcat全部阻塞在下面的堆栈上:

    "http-apr-7001-exec-2" #28 daemon prio=5 os_prio=31 tid=0x00007fe1e43db800 nid=0x660b runnable [0x0000000124629000]

    java.lang.Thread.State: RUNNABLE

    at org.apache.tomcat.jni.Socket.recvbb(Native Method)

    at org.apache.coyote.http11.InternalAprInputBuffer.fill(InternalAprInputBuffer.java:575)

    at org.apache.coyote.http11.InternalAprInputBuffer.parseHeader(InternalAprInputBuffer.java:464)

    at org.apache.coyote.http11.InternalAprInputBuffer.parseHeaders(InternalAprInputBuffer.java:312)

    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:969)

    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2442)

    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2431)

    - locked <0x000000079581e018> (a org.apache.tomcat.util.net.AprEndpoint$AprSocketWrapper)

    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

    at java.lang.Thread.run(Thread.java:745)

    在我们最近这一年内推出的ali-tomcat版本里已经不推荐apr模式了,因为它带来的性能上的提升与运维和维护的成本比不值。一方面在使用时tcnative这个本地库的版本要与tomcat的版本匹配,否则不同的版本可能不工作(曾经出现过这样的运维故障);二是我们曾遇到过apr导致jvm crash的情况;还有一个问题还是这个模块曾经被某个大牛修改过,继续维护的话团队里需要一个C/C++的人才行。

    当时的情况有些紧急,看到堆栈阻塞在apr的本地调用上,通过pstack查看libapr的调用底层阻塞在poll或epoll_wait上,一下子没有思路,只好先让应用方升级到新版本的ali-tomcat上,采用BIO或NIO模式看看。

    应用方切换到了新的版本后默认配置了BIO,线程池设置的是250,过不了一会儿大部分又阻塞在了下面的堆栈上:

    "http-bio-7001-exec-977" daemon prio=10 tid=0x00007f3e0bb96800 nid=0x6ff5 runnable [0x00007f3e054d3000]

    java.lang.Thread.State: RUNNABLE

    at java.net.SocketInputStream.socketRead0(Native Method)

    at java.net.SocketInputStream.read(SocketInputStream.java:129)

    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:516)

    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501)

    at org.apache.coyote.http11.Http11Processor.setRequestLineReadTimeout(Http11Processor.java:167)

    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:948)

    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:314)

    - locked <0x00000006ed322ed8> (a org.apache.tomcat.util.net.SocketWrapper)

    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)

    at java.lang.Thread.run(Thread.java:662)

    从这个堆栈来看直觉上以为在读数据,问了以下应用方,上游主要通过POST方式调用tomcat,每次数据大约在几K到几十K,当时没有细问,误以为真的是发送过来的数据量达,都阻塞在数据读取上。采取了增加线程数大小的做法,先调到1000,发觉稳定在700-800之间,后应用负责人有些不放心,又调到了1500.

    周末之后,应用虽然稳定,但BIO模式线程数开销的仍较大,通过ali-tomcat内置的监控功能可以看到线程池的状态:

    $ curl http://localhost:8006/connector/threadpool

    "http-bio-7001"

    -----------------------------------------------------------------

    | thread_count | thread_busy | min_pool_size | max_pool_size |

    -----------------------------------------------------------------

    | 1121 | 1091 | 10 | 1500 |

    -----------------------------------------------------------------

    BIO模式下在使用的线程有1091个,应用方尝试采用NIO模式,观察了一段时间,同等压力下,线程数有大幅度下降:

    $ curl http://localhost:8006/connector/threadpool

    "http-nio-7001"

    -----------------------------------------------------------------

    | thread_count | thread_busy | min_pool_size | max_pool_size |

    -----------------------------------------------------------------

    | 483 | 44 | 10 | 1500 |

    -----------------------------------------------------------------

    对于这么明显的下降,一方面怀疑BIO模式的瓶颈在哪儿,另一方面也觉得与业务的场景有关,正巧这个场景适用NIO模式。了解到他们使用了keep-alive,之前对于keep-alive的实现仅在NIO模式下有跟踪过,对于BIO和APR模式下如何实现的keep-alive没有很深入的了解,正好借这次问题排查详细的跟踪了一下另外两种模式下对keep-alive的实现。

    在说keep-alive的实现之前,先贴张之前分享ali-tomcat的ppt的一张图:

    f1a8347d7b4540c9376729edd9a4c21a.png

    这张表格引用自apache-tomcat官方网站,对于connector的三种模式有很好的对比,上次分享时着重讲NIO模式的实现,所以对NIO也不是完全非阻塞(读body和写response是模拟阻塞行为)的地方用红色突出了一下。这次我们先着重关注一下表格里的 “Wait for next Request” 这一项。它表示的是当开启keep-alive的情况下三种模式对等待下一次请求是否阻塞。

    1) BIO模式下的keep-alive实现:

    首先在BIO的专门负责socket建立的Acceptor线程的逻辑里,将socket封装成一个task(对应的是JIoEndpoint.SocketProcessor这个类)提交给线程池处理。而这个task(即SocketProcessor)的run方法逻辑大致是:

    try{

    ...

    state = handler.process(...); // 这里是具体的处理逻辑

    ...

    if (state == SocketState.OPEN){

    socket.setKeptAlive(true);

    socket.access();

    launch = true;

    }

    ...

    }finally{

    if(launch) {

    executor.execute(new SocketProcessor(...)); // 再次封装为同类型的task,并再次提交给线程池

    }

    }

    注意上面的逻辑,如果请求是开启keep-alive的话,socket在请求结束后仍处于OPEN状态,下一次请求仍可以复用当前socket而不必重新创建,在 finally 块里会判断连接状况如果是keep-alive会再次封装为同样的任务提交给线程池,重复这段逻辑,相当于一个循环,只不过每次执行的线程不一定相同。如果socket上已经没有请求了,最终socket会因超时或客户端close造成的EOFException而关闭。

    有一个简单的方法来判断keep-alive是否被有效利用,如果socket被复用得当的话,socket(对应的是SocketWrapper这个类)的实例数应该是大大小于请求task(对应的是SocketProcessor这个类)实例数。比如我们先模拟不复用scoket的情况:

    $ curl http://localhost:7001/main

    $ curl http://localhost:7001/main

    $ jmap -histo `pidof java` | sed -n -e '1,3p' -e '/SocketWrapper/p' -e '/SocketProcessor/p'

    num #instances #bytes class name

    ----------------------------------------------

    516: 2 128 org.apache.tomcat.util.net.SocketWrapper

    587: 4 96 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor

    上面执行了2次curl,建立了2次连接,因为http1.1默认就开启了keep-alive,所以根据前面try-finally里逻辑,一次连接的过程被创建的SocketProcessor实例数会比它实际的请求数多1个。所以这2次curl命令(每次的请求为1),没有复用socket,共创建了2个SocketWrapper实例和4个SocketProcessor实例。正好是2倍关系。

    如果复用socket,则SocketProcessor实例数应该比SocketWrapper的实例数多不止一倍,比如下面用zsh模拟10次请求:

    n=0;

    while (( n < 10 ));do

    n=$((n+1));

    echo -ne "GET /main HTTP/1.1\nhost: localhost:7001\n\n";

    sleep 1;

    done | telnet localhost 7001

    这10次请求是复用的同一个socket,在每次请求中间间隔了1秒,结束后再查看SocketProcessor和SocketWrapper的实例数:

    $ jmap -histo `pidof java` | sed -n -e '1,3p' -e '/SocketWrapper/p' -e '/SocketProcessor/p'

    num #instances #bytes class name

    ----------------------------------------------

    348: 11 264 org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor

    669: 1 64 org.apache.tomcat.util.net.SocketWrapper

    这次就一个socket的实例,task的实例数则是请求数+1,即11个。现实情况种这两个实例数差出1~2个数量级也常见(说明socket被复用的比较多)。

    BIO模式下keep-alive为什么线程利用率不高?

    再回到这次应用的例子中,为什么切换到BIO模式的情况下,被使用的线程数有1091个左右,而NIO的则只有44个,差距这么大的原因是什么?

    其实上面给出的官方对比的表格里已经有答案了,BIO在处理下一次请求的时候是阻塞的,而NIO是非阻塞的。所谓阻塞是线程会一直挂在这个连接上等待新的数据到来。

    正好这个应用的情况是开启keep-alive保持长连接,然后每隔1秒钟向tomcat发送一次数据。

    如果要模拟他们的情况,可以用下面的脚本:

    while :; do

    echo -ne "POST /main HTTP/1.1\nhost: localhost:7001\nContent-length:4\n\nData\n";

    sleep 1;

    done | telnet localhost 7001

    按说几K到几十K的数据最多不超过几十毫秒,也就是说socket在90%以上的时间是空闲状态,而BIO却一直有一个线程会阻塞在上面,白白浪费。

    这里有个细节,其实根据前边的JIoEndpoint.SocketProcessor的try-finally代码段,它不一定是阻塞在同一个线程上,取决于线程池的实现,但总会占用一个线程资源。现在看一下在等待下一次请求时的线程是怎么阻塞的:

    $ { echo -e "GET /main HTTP/1.1\nhost: localhost:7001\n"; sleep 10 } | telnet localhost 7001

    上面模拟了一次连接,请求结束后先不释放,保持10秒钟,以便我们执行jstack来看此时的线程情况:

    $ jstack `pidof java` | grep "socketRead0" -B2 -A10

    "http-bio-7001-exec-4" #28 daemon prio=5 os_prio=31 tid=0x00007f8a742c4000 nid=0x7d03 runnable [0x0000000128ceb000]

    java.lang.Thread.State: RUNNABLE

    at java.net.SocketInputStream.socketRead0(Native Method)

    at java.net.SocketInputStream.read(SocketInputStream.java:150)

    at java.net.SocketInputStream.read(SocketInputStream.java:121)

    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:516)

    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501)

    at org.apache.coyote.http11.Http11Processor.setRequestLineReadTimeout(Http11Processor.java:167)

    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:946)

    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)

    - locked <0x00000007973b0298> (a org.apache.tomcat.util.net.SocketWrapper)

    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

    看到这个堆栈信息和之前应用切换到BIO之后的情况一模一样,之前以为客户端发来的数据很多,这里是正在读取数据中,实际这里是没有请求数据过来,线程阻塞在这里等待数据。

    正是因为BIO的实现方式让线程一直阻塞在长连接上,而这应用的长连接在绝大部分时间内又是没有数据的,造成了线程的浪费,而APR和NIO则不会让线程一直阻塞在长连接上,提高了线程的利用率。

    2) APR模式下的keep-alive实现:

    APR有点类似NIO,也有Poller线程的角色。在处理下一次请求的时候,不会像BIO那样阻塞。下面看一下在处理socket时的大致逻辑,摘自AbstractHttp11Processor.process方法做了简化,这个类是BIO/NIO/APR三种模式处理socket逻辑时的基类,在开启keep-alive的情况下会一直循环:

    while ( keepAlive && !error && otherConditions ) {

    // Parsing the request header

    try {

    setRequestLineReadTimeout();

    if (!getInputBuffer().parseRequestLine(keptAlive)) {

    if (handleIncompleteRequestLineRead()) {

    break; //第一个break

    }

    }

    ...

    } catch (IOException e) {

    error = true;

    }

    ...

    prepareRequest();

    adapter.service(request, response); // 提交到后边的servlet容器

    endRequest();

    ...

    if (breakKeepAliveLoop(socketWrapper)) {

    break; //第二个break

    }

    }

    APR模式在处理完一次请求后,再次进入循环时会在第一个break点跳出(得不到下次请求),把线程让出来,后续socket再有请求时poller线程会再封装一个任务(对应SocketProcessor类),不过APR模式下acceptor在收到socket之后会先封装成一个SocketWithOptionsProcessor的task,它的作用只是把socket跟poller关联起来,真正处理请求时是靠poller。

    下面模拟3次请求:

    $ n=0;

    $ while (( n < 3 ));do

    n=$((n+1));

    echo -ne "GET /main HTTP/1.1\nhost: localhost:7001\n\n";

    sleep 1;

    done | telnet localhost 7001

    观察相关几个类的实例数:

    $ jmap -histo `pidof java` | sed -n -e '1,3p' -e '/SocketWrapper/p' -e '/Endpoint.*Processor/p'

    num #instances #bytes class name

    ----------------------------------------------

    619: 1 72 org.apache.tomcat.util.net.AprEndpoint$AprSocketWrapper

    620: 3 72 org.apache.tomcat.util.net.AprEndpoint$SocketProcessor

    975: 1 24 org.apache.tomcat.util.net.AprEndpoint$SocketWithOptionsProcessor

    socket所对应AprSocketWrapper实例为1,说明只有一个连接;SocketWithOptionsProcessor实例也为1,poller真正处理请求逻辑时还是用SocketProcessor封装的逻辑,这里3次请求对应3个实例数。注意有时候可能因为young-gc干扰你看到的实例数,可以把heap设置大一些避免。

    既然APR模式对下一次请求并不是阻塞,线程会释放出来,为何应用方还是出现了阻塞呢?因为当时的环境已经不能复现了,无法准确判断当时的网络情况,但APR模式在处理header和body的时候都是阻塞的,所以一种很大的可能是当时client发送数据时,没有发送完全,造成connector阻塞在jni.Socket.recvbb方法上。可以模拟一下这个情况:

    $ { echo -ne "POST /main HTTP/1.1\nhost: localhost:7001"; sleep 15 } | telnet localhost 7001

    上面模拟的POST请求没有发送完整,header部分还没有结束,这时通过jstack来看线程的情况:

    $ jstack `pidof java` | grep "recvbb" -B2 -A7

    "http-apr-7001-exec-6" #33 daemon prio=5 os_prio=31 tid=0x00007fc8b2044000 nid=0x7e07 runnable [0x0000000120a20000]

    java.lang.Thread.State: RUNNABLE

    at org.apache.tomcat.jni.Socket.recvbb(Native Method)

    at org.apache.coyote.http11.InternalAprInputBuffer.fill(InternalAprInputBuffer.java:575)

    at org.apache.coyote.http11.InternalAprInputBuffer.parseHeader(InternalAprInputBuffer.java:464)

    at org.apache.coyote.http11.InternalAprInputBuffer.parseHeaders(InternalAprInputBuffer.java:312)

    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:969)

    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)

    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.doRun(AprEndpoint.java:2442)

    at org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:2431)

    跟应用当时的情况是吻合的,当然如果client发送过程中如果body部分数据没有发送完整也会让tomcat阻塞在recvbb这个方法上。

    3) NIO模式下的keep-alive实现:

    NIO的大致结构也可以参考之前分享ali-tomcat的ppt里的图

    4778fce9f3dcdc5633e2f29fe77c71ce.png

    对于keep-alive情况下处理下一次请求,NIO跟APR类似,线程不会一直阻塞在socket上。对于header的处理,NIO也同样不会阻塞,只有在body的读取时,NIO采取模拟阻塞的方式。可以模拟一下,在一个servlet里对post过来的数据回写过去:

    public void doPost(HttpServletRequest request, HttpServletResponse resp) throws IOException {

    PrintWriter wr = resp.getWriter();

    BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));

    String line = null;

    while ((line = br.readLine()) != null) {

    wr.write(line);

    }

    wr.write("done");

    }

    模拟请求:

    $ {

    echo -ne "POST /main HTTP/1.1\nhost: localhost:7001\nContent-length:5\n\na";

    sleep 15

    } | telnet localhost 7001

    请求里描述的数据长度是5,但只给出了一个字符,出于数据未发送完的状态,这时来看服务器端线程状况:

    "http-nio-7001-exec-1" #26 daemon prio=5 os_prio=31 tid=0x00007f8693c52800 nid=0x7a07 waiting on condition [0x00000001268f6000]

    java.lang.Thread.State: TIMED_WAITING (parking)

    at sun.misc.Unsafe.park(Native Method)

    - parking to wait for <0x0000000795ca3b50> (a java.util.concurrent.CountDownLatch$Sync)

    at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)

    at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037)

    at java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1328)

    at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:277)

    at org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitLatch(NioEndpoint.java:1566)

    at org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitReadLatch(NioEndpoint.java:1568)

    at org.apache.tomcat.util.net.NioBlockingSelector.read(NioBlockingSelector.java:185)

    at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:246)

    at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:227)

    at org.apache.coyote.http11.InternalNioInputBuffer.readSocket(InternalNioInputBuffer.java:422)

    at org.apache.coyote.http11.InternalNioInputBuffer.fill(InternalNioInputBuffer.java:794)

    at org.apache.coyote.http11.InternalNioInputBuffer$SocketInputBuffer.doRead(InternalNioInputBuffer.java:819)

    at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:124)

    at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:346)

    at org.apache.coyote.Request.doRead(Request.java:422)

    at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:290)

    at org.apache.tomcat.util.buf.ByteChunk.substract(ByteChunk.java:449)

    at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:315)

    at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:200)

    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)

    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)

    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)

    - locked <0x0000000795c96f28> (a java.io.InputStreamReader)

    at java.io.InputStreamReader.read(InputStreamReader.java:184)

    at java.io.BufferedReader.fill(BufferedReader.java:161)

    at java.io.BufferedReader.readLine(BufferedReader.java:324)

    - locked <0x0000000795c96f28> (a java.io.InputStreamReader)

    at java.io.BufferedReader.readLine(BufferedReader.java:389)

    at org.r113.servlet3.MainServlet.doPost(MainServlet.java:37)

    线程并不是阻塞在原生的IO方法上,而是NioBlockingSelector.read方法上,这个方法从名字就可以看出它用NIO实现的阻塞式selector(里面的read和write方法注释也有明确说明);相当于通过锁的方式来模拟阻塞方式,正如之前表格里红色字体突出的。

    为什么NIO在读取body时要模拟阻塞?

    tomcat的NIO完全可以以非阻塞方式处理IO,为什么在读取body部分时要模拟阻塞呢?这是因为servlet规范里定义了ServletInputStream在读数据时是阻塞模式,这里相关的争论可以google。

    在servlet3.0里引入了异步,但仅针对传统IO,对应用来说仍有很多限制,所以servlet3.1又引入了非阻塞IO,但这要tomcat8才提供了。

    d0c1501a6d8bb921cf36400dc89de69f.png

    展开全文
  • vue keep alive详解

    2021-01-31 23:36:07
    vue keep alive详解

    基础

    <keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和 <transition> 相似,<keep-alive> 是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在组件的父组件链中。

    当组件在 <keep-alive> 内被切换,它的 activateddeactivated 这两个生命周期钩子函数将会被对应执行。

    在 2.2.0 及其更高版本中,activateddeactivated 将会在 <keep-alive> 树内的所有嵌套组件中触发。

    主要用于保留组件状态或避免重新渲染。

    <!-- 基本 -->
    <keep-alive>
      <component :is="view"></component>
    </keep-alive>
    
    <!-- 多个条件判断的子组件 -->
    <keep-alive>
      <comp-a v-if="a > 1"></comp-a>
      <comp-b v-else></comp-b>
    </keep-alive>
    
    <!--`<transition>` 一起使用 -->
    <transition>
      <keep-alive>
        <component :is="view"></component>
      </keep-alive>
    </transition>
    

    注意,<keep-alive> 是用在其一个直属的子组件被开关的情形。如果你在其中有 v-for 则不会工作。如果有上述的多个条件性的子元素,<keep-alive> 要求同时只有一个子元素被渲染。

    include and exclude (2.1.0 新增)

    include - 字符串,正则表达式或数组。只有名称匹配的组件会被缓存。
    exclude - 字符串,正则表达式或数组。任何名称匹配的组件都不会被缓存。

    includeexclude prop 允许组件有条件地缓存。二者都可以用逗号分隔字符串、正则表达式或一个数组来表示:

    <!-- 逗号分隔字符串 -->
    <keep-alive include="a,b">
      <component :is="view"></component>
    </keep-alive>
    
    <!-- 正则表达式 (使用 `v-bind`) -->
    <keep-alive :include="/a|b/">
      <component :is="view"></component>
    </keep-alive>
    
    <!-- 数组 (使用 `v-bind`) -->
    <keep-alive :include="['a', 'b']">
      <component :is="view"></component>
    </keep-alive>
    

    匹配首先检查组件自身的 name 选项,如果 name 选项不可用,则匹配它的局部注册名称 (父组件 components 选项的键值)。匿名组件不能被匹配。

    max (2.5.0 新增)

    max - 数字。最多可以缓存多少组件实例。

    一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉(LRU的策略置换缓存数据)。即max为1时,不缓存。

    LRU:内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。

    <keep-alive :max="10">
      <component :is="view"></component>
    </keep-alive>
    

    <keep-alive> 不会在函数式组件中正常工作,因为它们没有缓存实例。

    设计全站缓存

    我们既希望填写一半进入下一页面时能保留填写的数据,我们又希望新进入的表单是一个全新的表单页。

    换句话说,回到上一个页面时使用缓存,进入下一个页面时不使用缓存,

    再换句话说,所有页面都用缓存,只在后退(回到上一页)时移除当前页缓存,这样下一次前进(进入当前页)时因为没有缓存就自然使用全新页面,

    也就是说,只要实现后退(回到上一页)时移除当前页缓存这个功能,就可以了。

    在路由中定义位置

    这是一种缓存复用的思路,为了实现后退(回到上一页)时移除当前页缓存,因为想要实现动态确定用户的前进后退行为比较麻烦,所以,我们有个傻瓜式的方案:预测使用场景约定各路由页面的层级关系。

    比如,在 routes 定义里,我们可以这么定义各路由页:

    // 仅供参考,此处缺少路由组件定义
    // router/index.js
    routes: [
            {   path: '/', redirect:'/yingshou', },
            {   path: '/yingshou',                meta:{rank:1.5},},
            {   path: '/contract_list',           meta:{rank:1.5},},
            {   path: '/customer',                meta:{rank:1.5},},
            {   path: '/wode',                    meta:{rank:1.5},},
            {   path: '/yingfu',                  meta:{rank:1.5},},
            {   path: '/yingfu/pact_list',        meta:{rank:2.5},},
            {   path: '/yingfu/pact_detail',      meta:{rank:3.5},},
            {   path: '/yingfu/expend_view',      meta:{rank:4.5},},
            {   path: '/yingfu/jizhichu',         meta:{rank:5.5},},
            {   path: '/yingfu/select_pact',      meta:{rank:6.5},},
            {   path: '/yingfu/jiyingfu',         meta:{rank:7.5},},
        ]
    

    核心的思路是,在定义路由时,在 meta 中定义一个 rank 字段来声明该路由的页面优先级, 比如 1.5 标识第 1 层如首页,2.5 表示第 2 层如商品列表页, 3.5标识第 3 层商品详情页,以此类推。

    如果大家同在一层,也可以通过 1.4 和 1.5 这样小数位来约定先后层级。

    总之,我们期望的是,从第1层进入第2层是前进,从第3层回到第2层是后退。

    在路由跳转里动态判断移除缓存

    使用Vue.mixin的方法拦截了路由离开事件,并在该拦截方法中实现后退时销毁页面缓存。

    // main.js
    Vue.mixin({
        beforeRouteLeave:function(to, from, next){
            if (from.meta.rank && to.meta.rank && from.meta.rank > to.meta.rank){
              //此处判断是如果返回上一层,你可以根据自己的业务更改此处的判断逻辑,酌情决定是否摧毁本层缓存。
                if (this.$vnode && this.$vnode.data.keepAlive)
                {
                    if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache)
                    {
                        if (this.$vnode.componentOptions)
                        {
                            var key = this.$vnode.key == null
                                        ? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '')
                                        : this.$vnode.key;
                            var cache = this.$vnode.parent.componentInstance.cache;
                            var keys  = this.$vnode.parent.componentInstance.keys;
                            if (cache[key])
                            {
                                if (keys.length) {
                                    var index = keys.indexOf(key);
                                    if (index > -1) {
                                        keys.splice(index, 1);
                                    }
                                }
                                delete cache[key];
                            }
                        }
                    }
                }
                this.$destroy();
            }
            next();
        },
    });
    

    缓存给页面复用带来的问题

    即使是路由页面复用了缓存,也只是复用了缓存的组件和数据,在实际场景中,从列表 A 进入详情 B 再进入列表 C ,请问 列表 C 和列表 A 是同一个路由页,但他们的数据会一样吗?应该一样吗?

    看起来,我们得到了一个新结论,缓存页的数据也不可靠啊。

    缓存的组件被复用时会触发 activated 事件,非缓存组件则会在创建时触发 created mounted 等一大堆事件,而同一个页面列表 A 进列表 B,因为 url 参数不同,也会触发beforeRouteUpdate事件。

    我们在捕捉到页面载入的事件后去拉取数据更新页面。

    这里提供一个笨办法,事件该捕捉咱还是要捕捉,只是这是否去做拉取数据这个动作,咱可以有待商榷。

    在需要的路由页统一注册一个方法,就叫pageenter吧,不管从啥情况进来的,咱都去触发这个方法。

    // list.vue
    methods:{
        pageenter:function(){
           //此处拉取数据
           //this.$http.get(....)
        },
    }
    

    在main.js里,可以用Vue.mixin拦截上文提到的三种事件,来触发 pageenter 方法。

    // main.js
    Vue.mixin({
    	/*初始化组件时,触发pageenter方法*/
    	created:function(){
    	   if (this.pageenter){
    	       this.pageenter();
    	   }
    	},
    	/*激活当前组件时*/
    	activated:function(){
    	   if (this.pageenter){
    	       this.pageenter();
    	   }
    	},
    	/*在同一组件中,切换路由(参数变化)时*/
    	beforeRouteUpdate:function(to, from, next){
    	    if (this.pageenter){
    	        this.$nextTick(()=>{
    	            this.pageenter();
    	        });
    	    }
    	    next();
    	},
    });
    

    原理解析

    源码剖析

    keep-alive.js内部还定义了一些工具函数,我们按住不动,先看它对外暴露的对象

    // src/core/components/keep-alive.js
    export default {
      name: 'keep-alive',
      abstract: true, // 判断当前组件虚拟dom是否渲染成真实dom的关键
      props: {
          include: patternTypes, // 缓存白名单
          exclude: patternTypes, // 缓存黑名单
          max: [String, Number] // 缓存的组件
      },
      created() {
         this.cache = Object.create(null) // 缓存虚拟dom
         this.keys = [] // 缓存的虚拟dom的键集合
      },
      destroyed() {
        for (const key in this.cache) {
           // 删除所有的缓存
           pruneCacheEntry(this.cache, key, this.keys)
        }
      },
     mounted() {
       // 实时监听黑白名单的变动
       this.$watch('include', val => {
           pruneCache(this, name => matches(val, name))
       })
       this.$watch('exclude', val => {
           pruneCache(this, name => !matches(val, name))
       })
     },
    
     render() {
        // 先省略...
     }
    }
    

    可以看出,与我们定义组件的过程一样,先是设置组件名为keep-alive,其次定义了一个abstract属性,值为true。这个属性在vue的官方教程并未提及,却至关重要,后面的渲染过程会用到。props属性定义了keep-alive组件支持的全部参数。

    keep-alive在它生命周期内定义了三个钩子函数:

    • created
      初始化两个对象分别缓存VNode(虚拟DOM)和VNode对应的键集合
    • destroyed
      删除this.cache中缓存的VNode实例。我们留意到,这不是简单地将this.cache置为null,而是遍历调用pruneCacheEntry函数删除。
    // src/core/components/keep-alive.js
    function pruneCacheEntry (
      cache: VNodeCache,
      key: string,
      keys: Array<string>,
      current?: VNode
    ) {
     const cached = cache[key]
     if (cached && (!current || cached.tag !== current.tag)) {
        cached.componentInstance.$destroy() // 执行组件的destroy钩子函数
     }
     cache[key] = null
     remove(keys, key)
    }
    
    

    删除缓存的VNode还要对应组件实例的destory钩子函数

    • mounted
      在mounted这个钩子中对include和exclude参数进行监听,然后实时地更新(删除)this.cache对象数据。pruneCache函数的核心也是去调用pruneCacheEntry
    function pruneCache (keepAliveInstance: any, filter: Function) {
      const { cache, keys, _vnode } = keepAliveInstance
      for (const key in cache) {
        const cachedNode: ?VNode = cache[key]
        if (cachedNode) {
          const name: ?string = getComponentName(cachedNode.componentOptions)
          if (name && !filter(name)) {
            pruneCacheEntry(cache, key, keys, _vnode)
          }
        }
      }
    }
    
    • render
    render () {
      const slot = this.$slots.defalut
      const vnode: VNode = getFirstComponentChild(slot) // 找到第一个子组件对象
      const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
      if (componentOptions) { // 存在组件参数
        // check pattern
        const name: ?string = getComponentName(componentOptions) // 组件名
        const { include, exclude } = this
        if (// 条件匹配
          // not included
          (include && (!name || !matches(include, name)))||
          // excluded
            (exclude && name && matches(exclude, name))
        ) {
            return vnode
        }
        
        const { cache, keys } = this
        // 定义组件的缓存key
        const key: ?string = vnode.key === null 
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') 
        : vnode.key
         if (cache[key]) { // 已经缓存过该组件
            vnode.componentInstance = cache[key].componentInstance
            remove(keys, key)
            keys.push(key) // 调整key排序
         } else {
            cache[key] = vnode // 缓存组件对象
            keys.push(key)
            if (this.max && keys.length > parseInt(this.max)) {
              //超过缓存数限制,将第一个删除
              pruneCacheEntry(cahce, keys[0], keys, this._vnode)
            }
         }
         
          vnode.data.keepAlive = true //渲染和执行被包裹组件的钩子函数需要用到
     
     }
     return vnode || (slot && slot[0])
    }
    
    • 第一步:获取keep-alive包裹着的第一个子组件对象及其组件名;
    • 第二步:根据设定的黑白名单(如果有)进行条件匹配,决定是否缓存。不匹配,直接返回组件实例(VNode),否则执行第三步;
    • 第三步:根据组件ID和tag生成缓存Key,并在缓存对象中查找是否已缓存过该组件实例。如果存在,直接取出缓存值并更新该key在this.keys中的位置(更新key的位置是实现LRU置换策略的关键),否则执行第四步;
    • 第四步:在this.cache对象中存储该组件实例并保存key值,之后检查缓存的实例数量是否超过max设置值,超过则根据LRU置换策略删除最近最久未使用的实例(即是下标为0的那个key);
    • 第五步:最后并且很重要,将该组件实例的keepAlive属性值设置为true。

    重头戏:渲染

    Vue的渲染过程

    借助一张图看下Vue渲染的整个过程:
    vue渲染过程.png
    Vue的渲染是从图中render阶段开始的,但keep-alive的渲染是在patch阶段,这是构建组件树(虚拟DOM树),并将VNode转换成真正DOM节点的过程。

    简单描述从render到patch的过程
    我们从最简单的new Vue开始:

    import App from './App.vue'
    
    new Vue({
        render: h => h(App)
    }).$mount('#app')
    
    • Vue在渲染的时候先调用原型上的_render函数将组件对象转化成一个VNode实例;而_render是通过调用createElement和createEmptyVNode两个函数进行转化;
    • createElement的转化过程会根据不同的情形选择new VNode或者调用createComponent函数做VNode实例化;
    • 完成VNode实例化后,这时候Vue调用原型上的_update函数把VNode渲染成真实DOM,这个过程又是通过调用patch函数完成的(这就是patch阶段了)

    用一张图表达:
    渲染过程.png

    keep-alive组件的渲染

    我们用过keep-alive都知道,它不会生成真正的DOM节点,这是怎么做到的?

    // src/core/instance/lifecycle.js
    export function initLifecycle (vm: Component) {
        const options = vm.$options
        // 找到第一个非abstract父组件实例
        let parent = options.parent
        if (parent && !options.abstract) {
            while (parent.$options.abstract && parent.$parent) {
                  parent = parent.$parent
            }
            parent.$children.push(vm)
        }
        vm.$parent = parent
        // ...
    }
    

    Vue在初始化生命周期的时候,为组件实例建立父子关系会根据abstract属性决定是否忽略某个组件。在keep-alive中,设置了abstract:true,那Vue就会跳过该组件实例。

    最后构建的组件树中就不会包含keep-alive组件,那么由组件树渲染成的DOM树自然也不会有keep-alive相关的节点了。

    keep-alive包裹的组件是如何使用缓存的?
    在patch阶段,会执行createComponent函数:

    // src/core/vdom/patch.js
    function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {
        let i = vnode.data
        if (isDef(i)) {
            const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
            if (isDef(i = i.hook) && isDef(i = i.init)) {
                i(vnode, false)
            }
            if (isDef(vnode.componentInstance)) {
                initComponent(vnode, insertedVnodeQueue)
                insert(parentElm, vnode.elm, refElm) // 将缓存的DOM(vnode.elem) 插入父元素中
                if (isTrue(isReactivated)) {
                    reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)
                }
                return true
            }
        }
    }
    
    • 在首次加载被包裹组件时,由keep-alive.js中的render函数可知,vnode.componentInstance的值是undfined,keepAlive的值是true,因为keep-alive组件作为父组件,它的render函数会先于被包裹组件执行;那么只执行到i(vnode,false),后面的逻辑不执行;
    • 再次访问被包裹组件时,vnode.componentInstance的值就是已经缓存的组件实例,且keepAlive为true,那么会执行reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm)逻辑,这样就直接把上一次的DOM插入到父元素中。

    不可忽视:钩子函数

    只执行一次的钩子

    一般的组件,每一次加载都会有完整的生命周期,即生命周期里面对应的钩子函数都会被触发,为什么被keep-alive包裹的组件却不是呢?
    被缓存的组件实例会为其设置keepAlive= true,而在初始化组件钩子函数中:

    // src/core/vdom/create-component.js
    const componentVNodeHooks = {
        init (vnode: VNodeWithData, hydrating: boolean): ?boolean{
            if (
             vnode.componentInstance &&       
             !vnode.componentInstance._isDestroyed &&
             vnode.data.keepAlive
            ) {
              // keep-alive components, treat as a patch
              const mountedNode:any = vnode
              componentVNodeHooks.prepatch(mountedNode, mountedNode)
            } else {
              const child = vnode.componentInstance = createComponentInstanceForVnode (vnode, activeInstance)
              child.$mount(hydrating ? vnode.elm : undefined, hydrating)
            }
        }
    }
    

    可以看出,当vnode.componentInstance和keepAlive同时为true时,不再进入$mount过程,那mounted之前的所有钩子函数(beforeCreate、created、mounted)都不再执行。

    可重复的activated

    在patch的阶段,最后会执行invokeInsertHook函数,而这个函数就是去调用组件实例(VNode)自身的insert钩子:

    // src/core/vdom/patch.js
    function invokeInsertHook (vnode, queue, initial) {
          if (isTrue(initial) && isDef(vnode.parent)) {
              vnode.parent.data.pendingInsert = queue
          } else {
             for(let i = 0; i < queue.length; ++i) {
                    queue[i].data.hook.insert(queue[i]) // 调用VNode自身的insert钩子函数
             }
          }
    }
    

    再看insert钩子:

    const componentVNodeHooks = {
          // init()
         insert (vnode: MountedComponentVNode) {
               const { context, componentInstance } = vnode
               if (!componentInstance._isMounted) {
                     componentInstance._isMounted = true
                     callHook(componentInstance, 'mounted')
               }
               if (vnode.data.keepAlive) {
                     if (context._isMounted) {
                         queueActivatedComponent(componentInstance)
                     } else {
                          activateChildComponent(componentInstance, true/* direct */)
                     }
              }
             // ...
         }
    }
    

    在这个钩子里面,调用了activateChildComponent函数递归地去执行所有子组件的activated钩子函数:

    // src/core/instance/lifecycle.js
    export function activateChildComponent (vm: Component, direct?: boolean) {
      if (direct) {
        vm._directInactive = false
        if (isInInactiveTree(vm)) {
          return
        }
      } else if (vm._directInactive) {
        return
      }
      if (vm._inactive || vm._inactive === null) {
        vm._inactive = false
        for (let i = 0; i < vm.$children.length; i++) {
          activateChildComponent(vm.$children[i])
        }
        callHook(vm, 'activated')
      }
    }
    

    相反地,deactivated钩子函数也是一样的原理,在组件实例(VNode)的destroy钩子函数中调用deactivateChildComponent函数。

    展开全文
  • Hello,你好呀,我是灰小猿!一个超会写bug的程序猿! 用坚持缔造技术、用指尖敲动未来! 和很多小伙伴们一样,我也是一名奔波在...一、“持久连接(Keep-Alive)和非持久连接(非Keep-Alive)的区别,他们对服务性能.

    Hello,你好呀,我是灰小猿!一个超会写bug的程序猿!
    用坚持缔造技术、用指尖敲动未来!
    和很多小伙伴们一样,我也是一名奔波在Java道路上的“创造者”。也想靠技术来改未来,改变世界!因为我们坚信每一次敲动键盘都能让生活变得更智能、世界变得更有趣
    在此专栏《Java核心面试宝典》记录我们备战梦想的【day 17】

    在这里插入图片描述

    今天我来和大家讲解一下HTTP协议中有关持久层连接和非持久层连接的几道常见面试题。

    一、“持久连接(Keep-Alive)和非持久连接(非Keep-Alive)的区别,他们对服务性能有影响吗?”

    在最早期的HTTP/1.0中,**浏览器每次发起HTTP请求都要与服务器建立一个TCP连接,服务器完成请求处理之后就会立即断开这个TCP连接,**因此服务器并不会跟踪每一个用户,同时也不会记录之前的请求信息。然而创建和关闭连接是需要消耗大量的资源和时间的,为了减少资源的消耗,缩短响应的时间,就需要重用连接。

    所以在之后的HTTP/1.1版本中就默认使用持久连接,而在之前的版本中默认使用的是非持久连接,

    对于非Keep-Alive来说, 对于每一个浏览器请求,我们必须为每一个请求的对象建立和维护一个全新的连接,同时对于每一个这样的连接,客户机和服务器都要分配TCP的缓冲区和变量,这就给服务器带来了严重的负担,因为一台web服务器可能服务数以百计的客户机请求,如果每一次请求对要不断的断开和重新建立连接,那么将会对服务器资源造成巨大的损失!

    而在Keep-Alive方式下, 服务器响应后保持该TCP连接打开,这样在同一台客户机和服务器之间进行的后续请求和响应报文就都可以通过这个TCP连接传输,甚至位于同一台服务器的多个web页面在向同一个客户机发送时,可以在单个持久的TCP连接上进行。

    我们可以通过下面这张图来表示Keep-Alive和非Keep-Alive连接。
    请添加图片描述但是对于Keep-Alive就没有缺点了吗?并不是的!

    二、追问:如何避免持久连接时系统资源被无效占用?

    当我们长时间保持TCP连接时容易造成系统资源被无效占用, 若对Keep-Alive模式配置不当,这样将可以会造成比非Keep-Alive方式更大的损失。因此我们需要正确的设置Keep-Alive timeout参数,当TCP连接传送玩最后一个HTTP响应后,该连接会保持Keep-Alive_timeout秒,之后断开并关闭这个连接!这样就会很好的避免系统资源被长时间无效的占用。

    三、追问:那么如果使用了旧版本的HTTP,如何维持持久连接呢?

    如果想要在旧版本的HTTP协议上维持持久连接,则需要指定connection的首部字段为Keep-Alive,来告诉对方这个请求完成后不要关闭,下一次还要使用这个连接进行通信。

    四、HTTP长连接和短连接的使用场景是什么?

    长连接: 长连接多用于操作频繁点对点的通讯,而且客户端连接数目较少的情况。如即时通讯、网络游戏等。

    短连接: 用户数目较多的web网站通常使用短连接,例如京东、淘宝这样的大型网站一般客户端数量达到千万级甚至上亿,若使用长连接势必会造成大量的资源被无效占用,所以一般使用短连接,请求处理完成即关闭!

    今日总结

    今天的文章主要和大家讲解了HTTP协议的持久连接和非持久连接的区别和使用,我们需要了解持久连接和非持久连接的特点两者之间如何转换使用场景等。

    如果小伙伴们有遇到其他相关的面试题,欢迎在评论区留言提出,我会把大家提出的总结到文章内, 欢迎小伙伴们一起评论区打卡学习!小伙伴们可也在左方加我好友一起探讨学习!

    我是 灰小猿 ,我们下期见!
    请添加图片描述

    展开全文
  • Oracle KEEP Pool

    2021-05-07 09:55:34
    Oracle Tips by Burleson ConsultingOracle KEEP PoolA DBA can easily write a script that automatically identifies candidates for the KEEP pool and generates the syntax to move the tables into the pool....
  • # ${android_sdk}/tools/proguard/proguard-android.txt# Understand the @Keep support annotation.-keep class android.support.annotation.Keep-keep @android.support.annotation.Keep class * {*;}-keepclasses...
  • 如果不添加改行会导致我们的@Keep注解失效 -keepattributes *Annotation* -keep @android.support.annotation.Keep class **{ @android.support.annotation.Keep ; } 我们继续修改Test类,这次我们多加了点东西...
  • 聊聊vue中的keep-alive

    2021-11-27 00:04:27
    一、什么是keep-alive?官方介绍就是:<keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。和<transition>相似,...
  • 数据迁移后性能受到影响,需要将老数据库中keep到内存中的表在新库中keep到内存中,使用如下方法。新库设置db_keep_cache_size为适当值,这个值的大小不能小于需要keep的表的大小。查看老库中需要keep的表信息:...
  • 这篇文章是「Java 混淆那些事」的第三篇,我们来真枪真刀的干一下子,用实际行动验证了解一下 ProGuard 的 Keep 语法,这篇代码偏多,希望大家好好理解。阅读提示:上半部分纯属个人总结,不明白请看下半部分的例子...
  • 都知道keep-alive是用于vue中组件缓存的,那内部如何实现缓存的呢,一起来看看吧 先放简单的文章导图: 1.keep-alive 在vue中起到什么作用 先来看看官网对keep-alive的一些功能说明: 组件之间切换的时候,你有时...
  • Keep于17年初接入In-App Purchase,功能上线后暴漏出严重的丢单问题,丢单概率大概在百分之一。丢单问题在多人多次优化后仍未能解决,成为Keep客户端的顽疾。直至最近的两次优化彻底根治了丢单问题。本文中笔者将循...
  • HTTP 的 Keep-Alive模式

    2021-04-29 07:32:25
    一、Keep-Alive模式1、 HTTP协议采用“请求-...当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重...
  • vue 删除Keep-alive缓存

    2021-11-30 16:53:40
    vue删除Keep-alive缓存 keep-alive简单介绍: 我们项目中为了用户有更好的体验/页面性能,大多数地方都会使用到keep-alive,这个是vue提供的组件,它会将它的子组件进行数据缓存/行为缓存。 大致原理就是当离开当前...
  • 于是乎,开始用抓包工具,结合网络库log进行排查,发现虽然在发起网络请求时header增加了"Connection":“Keep-Alive”,使用charles抓包工具查看请求,实际上却没有保持连接,每次都会重建建立网络连接,这个过程...
  • 1. HTTP协议关于keep-alive的说明 参考“Hypertext Transfer Protocol – HTTP/1.1-8.1 Persistent Connections”( https://tools.ietf.org/html/rfc2616#section-8.1 )。 在持久化连接出现之前,每次访问URL都需要...
  • http keep-alive实现了对同一tcp连接的复用,即可以在一个TCP连接中发送多个HTTP请求,这种技术叫做HTTP复用(HTTP Multiplexing),与TCP连接复用是完全不同的技术,不要混淆了。HTTP复用(对同一tcp连接的复用)只是...
  • HTTP Keep-Alive详解[转]

    2021-03-14 16:45:15
    服务器的连接,在这个连接上发送请求,然后接收请求。这样的模式有一个很大的优点就是,它很简单,很容易理解和编程实现;它也有一个很大的缺点就是,它效率很低,因此Keep-Alive被...Keep-Alive功能使客户端到服务...
  • Oracle将表keep到内存

    2021-05-02 08:26:29
    一、引言:有时候一些基础表...二、关于keep内存的几个参数下面了解一下具体和CACHE有关的几个概念,即DB_CACHE中的几个pool:DB_CACHE_SIZE:指定缺省的buffer pool的大小,以字节为单位。DB_KEEP_CACHE_SIZE:指定...
  • keep-alive 参考文档 Props: include - 字符串或正则表达式。只有名称匹配的组件会被缓存。 exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。 max - 数字。最多可以缓存多少组件实例。 用法...
  • 仿keep思路

    2021-05-27 09:59:11
    创建一个视频播放器,播放准备好的视频NSString *url = [[NSBundle mainBundle] pathForResource:@"1" ofType:@"mp4"];self.mp4Item = [AVPlayerItem playerItemWithURL:[NSURL fileURLWithPath:url]];...
  • 展开全部keep you updated正确。释义:及时向你更62616964757a686964616fe58685e5aeb931333363396434新情况短语:I will keep you updated我将和你保持联络 ; 我会及时更新给你例句:1、I amponderinglivingsolely ...
  • keep you updated正确。释义:及时向你更新情况短语:I will keep you updated我将和你保持联络 ; 我会及时更新给你例句:1、I amponderinglivingsolely by theFoodRules forAugustsoIwillkeepyouupdatedonthat.我在...
  • 文章目录一、Keep-alive 是什么二、使用场景三、原理分析 一、Keep-alive 是什么 keep-alive是vue中的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM keep-alive 包裹动态组件时,会缓存不...
  • 这个不是业务的要求,但是看到每次进入页面就重新渲染DOM然后再获取数据更新DOM,觉得作为一个前端工程师有必要优化下的加载逻辑,正好vue提供了keep-alive的功能,所以就试用了下。当然,干任何事儿都不会一帆风顺...
  • keep you updated正确。释义:及时向你更新情况短语:I will keep you updated我将和你保持联络 ; 我会及时更新给你例句:1、I amponderinglivingsolely by theFoodRules forAugustsoIwillkeepyouupdatedonthat.我在...
  • ] 我们简单分析一下两端的输出结果: 从server端打印的请求的头部字段来看,客户端发来的请求header中并没有显式包含Connection: keep-alive,而仅有Accept-Encoding和User-Agent两个header字段; server端处理的5个...
  • keep app带来的思考

    2021-08-07 05:45:00
    这两天用keep的app做跑前热身、燃脂跑和跑后拉伸进行锻炼。感觉太棒了!为什么?1.跑前热身和跑后拉伸有专人示范及计时(每个动作不会超过1分钟),你不需要去想要做什么动作,只要跟着做就ok,简单、方便、容易做到。...
  • keep-alive 源码的理解

    2021-08-12 11:16:17
    keep-alive 概念 keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。 keep-alive ...
  • 这个不是业务的要求,但是看到每次进入页面就重新渲染DOM然后再获取数据更新DOM,觉得作为一个前端工程师有必要优化下的加载逻辑,正好vue提供了keep-alive的功能,所以就试用了下。当然,干任何事儿都不会一帆风顺...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 542,914
精华内容 217,165
关键字:

keep