如何搭建MQTT测试服务器

thbcm阅读(234)

这段时间有个同事在开发一款物联网硬件,这需要将采集的传输到客户的MQTT服务器上,不过目前还在调试阶段,连接到对方服务器测试的话很不方便,于是他就想要搭建一个MQTT测试服务器。在网上搜索了常用的MQTT代理服务器,最终决定采用ActiveMQ Artemis

ActiveMQ介绍

Apache ActiveMQ 是一款基于Java的消息服务器,支持多种协议可以使用CC++Python.Net等进行连接。ActiveMQ 官网介绍目前提供2个版本下载,ActiveMQ 经典版和ActiveMQ Artemis,其中Artemis为下一代高性能,非阻塞架构,基于事件驱动的消息系统。

ActiveMQ Artemis 安装

为了测试方便我们将ActiveMQ Artemis部署在windows平台,我们在activemq官网下载apache-artemis-2.14.0-bin.zip ,目前最新版本为2.14.0 (July 20, 2020)

我们将下载的ZIP压缩包解压到D:\apache-artemis-2.14.0-boker。创建一个broker实例,实例需要新建一个文件夹F:\apache-artemis-2.14.0-boker,进入D:\apache-artemis-2.14.0-boker\bin目录执行(artemis create D:\apache-artemis-2.14.0-boker)创建broker实例,执行artemis create命令提示需要输入账号密码,按照提示输入即可。

创建成功后,执行artemis run启动broker实例。实例启动成功后,我们可以使用创建实例时输入的的账号密码登入管理面板。

ActiveMQ Artemis 配置

我们可以修改MQTT的默认端口,配置文件位置:实例文件夹\etc\broker.xml <!– MQTT Acceptor –> <acceptor name=”mqtt”>tcp://0.0.0.0:1883 我们将1883修改为我们需要的端口即可

修改管理面板WEB端口,配置文件位置:安装目录 \etc\bootstrap.xml <web bind=”localhost:8161″ path=”web”> 默认端口8161修改为你想要的端口

MQTT客户端测试

我们使用通讯猫调试软件,订阅硬件发布的主题消息。

同事说能否自己弄一个简单的MQTT客户端,实时解析订阅收到的消息。客户端程序的话,用 C#winform写很方便,手撸一个MQTT客户端代码(使用MQTTnet)分分钟搞定。

以上就是关于如何搭建MQTT测试服务器的相关介绍了,希望对大家有所帮助。

Linux高性能I/O框架库Libevent介绍

thbcm阅读(240)

这篇文章主要讲一下Libevent库的内容,顺便对I/O库整体做个介绍。

Linux服务器程序必须处理的三类事件:

  • I/O事件
  • 信号
  • 定时事件

在处理这三类事件时我们通常需要考虑如下三个问题:

  • 统一事件源。很明显,统一处理这三类事件既能使代码简单易懂,又能避免一些潜在的逻辑错误。
  • 可移植性。不同的操作系统具有不同的I/O复用方式,比如Solarisdev/poll文件,FressBSDkqueue机制,Linuxepoll系统调用
  • 对并发编程的支持,在多进程和多线程环境下,我们需要考虑各执行实体如何协同处理客户连接、信号和定时器,以避免竞态条件。

幸运的是,开源社区提供了很多优秀的I/O框架库,他们不仅解决了上述问题,让开发者可以将精力完全放在程序的逻辑上,而且稳定性、性能等各方面都相当出色。而Libevent就是其中相对轻量级的框架库。

I/O框架库概述

I/O框架库以库函数的形式,封装了较为底层的系统调用,给应用程序提供了一组更便于使用的接口。这些库函数往往比程序员自己实现的同样功能的函数更合理、更高效、且更健壮。因为它们经受住了真实网络环境下的高压测试,以及时间的考验。

各种I/O框架库的实现原理基本相似,要么以Reactor模式实现,要么以Procator模式实现(高性能服务器程序框架 – 两种高效的事件处理模式),要么同时以这两种模式实现。举例来说,基于Reactor模式的I/O框架库包含如下几个组件:

  • 句柄Handle
  • 事件多路分发器EventDemultiplexer
  • 事件处理器Eventhandler
  • 具体的事件处理器ConcreteEventHandler
  • Reactor

(推荐教程:Linux教程

这些组件关系如下图:

  1. 句柄: I/O框架库要处理的对象,即I/O事件、信号和定时事件,统一称为事件源。一个事件源通常和一个句柄绑定在一起。句柄的作用是,当内核检测到就绪事件时,它将通过句柄来通知应用程序这一事件。在Linux环境下,I/O事件对应的句柄是文件描述符,信号事件对应的句柄就是信号值。
  2. 事件多路分发器:事件的到来是随机的、异步的。我们无法预知程序何时收到一个客户连接请求,又亦活收到一个暂停信号。所以程序需要循环地等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术来实现。I/O框架库一般将系统支持的各种I/O复用系统调用封装成统一的接口,称为事件多路分发器。事件多路分发器的demultiplex方法是等待事件的核心函数,其内部调用的是selectpollepoll_wait等函数。此外事件多路分发器还需实现register_eventremove_event方法,以供调用者往事件多路分发器中添加事件和从事件多路分发器中删除事件。
  3. 事件处理器和具体时间处理器:事件处理器执行事件对应的业务逻辑。它通常包含一个或多个handle_event回调函数,这些回调函数在事件循环中被执行。I/O框架库提供的事件处理器通常是一个接口,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般被声明为需函数,以支持用户的扩展。此外,事件处理器一般还提供一个get_handle方法,它返回与该事件处理器关联的句柄。那么事件处理器和句柄有什么关系?当时间多路分发器检测到有事件发生时,它是通过句柄来通知应用程序的。因此,我们必须将事件处理器和句柄绑定,才能在事件发生时获取到正确的事件处理器。
  4. Reactor:Reactor是I/O框架的核心。它提供的几个主要方法是:
    • handle_events:该方法执行事件循环。它重复如下过程:等待事件,然后依次处理所有就绪事件对应的事件处理器。
    • register_handler: 该方法调用事件多路分发器的register_event方法来往事件多路分发器中注册一个事件。 –remove_handler:该方法调用事件多路分发器的remove_event方法来往删除事件多路分发器中注册一个事件。

I/O框架库的工作时序如下:

Libevent源码分析

Libevent是开源社区的一款高性能的I/O框架库,具有如下特点:

  • 跨平台支持
  • 统一事件源
  • 线程安全
  • 基于Reactor模式的实现

(推荐微课:Linux微课

一个实例

下面是用Libevent库实现的一个“Hello World”程序。

include <sys/signal.h>

#include <event2/event.h>


void signal_cb(int fd, short event, void *argc)
{
    struct event_base* base = (event_base*)argc;
    struct timeval delay = {2, 0};
    printf("Caught an interrupt signal; exiting cleanly in two seconds....\n");
    event_base_loopexit(base, &delay);
}


void timeout_cb(int fd, short event, void* argc)
{
    printf("timeout\n");
}


int main(int argc, char const *argv[])
{
    struct event_base* base = event_base_new();
    struct event* signal_event = evsignal_new(base, SIGINT, signal_cb, base);
    event_add(signal_event, NULL);


    timeval tv = {1, 0};
    struct event* timeout_event = evtimer_new(base, timeout_cb, NULL);
    event_add(timeout_event, &tv);


    event_base_dispatch(base);


    event_free(timeout_event);
    event_free(signal_event);
    event_base_free(base);


    return 0;
}

上述代码虽然简单,但却基本描述了Libevent库的主要逻辑:

  1. 调用event_base_new函数创建event_base对象。一个event_base相当于一个Reactor实例。
  2. 创建具体的事件处理器,并设置它们所从属的Reactor实例。evsignal_newevtimer_new分别用于创建信号事务处理器和定时事件处理器。它们是定义在如下:

define evsignal_new(b, x, cb, arg) \

    event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
#define evtimer_new(b, cb, arg)     event_new((b), -1, 0, (cb), (arg))

可见,他们的统一入口是event_new函数,即用于创建通用事件处理器的函数,定义如下:

event_new(struct event_base base, evutil_socket_t fd, short events, void (cb)(evutil_socket_t, short, void ), void arg)其中,base参数指定行

其中:

  • base参数指定新创建的事件处理器从属的Reactor
  • fd参数指定与事件处理器关联的句柄。创建I/O事件处理器时,应该给fd参数传递文件描述符;创建信号事件处理器时,应该给fd参数传递信号值,比如之前实例代码中的SIGINT;创建定时事件处理器时则应该给fd参数传递-1
  • events参数指定事件类型,定义如下:
    #define EV_TIMEOUT  0x01   /*定时事件*/
    #define EV_READ     0x02         /*可读事件*/
    #define EV_WRITE    0x04        /*可写事件*/
    #define EV_SIGNAL   0x08       /*信号事件*/
    #define EV_PERSIST  0x10     /*永久事件*/
    /*边缘触发事件,需要I/O复用系统调用支持,比如epoll */
    #define EV_ET       0x20

上述代码中,EV_PERSIST的作用是:事件被触发后,自动重新对这个event调用event_add函数。

  • cb参数指定目标事件对应的回调函数,相当于事件处理器handle_event方法.
  • arg则是Reactor传递给回调函数的参数。

event_new函数成功时返回一个event类型的对象,也就是Libevent的事件处理器。Libevent用单词“event”来描述事件处理器,而不是事件,所以约定如下:

  • 事件指的是一个句柄上绑定的事件,比如文件描述符 0 上的可读事件
  • 事件处理器,也就是event结构提类型的对象,除了包含事件必须具备的两个要素(句柄和事件类型)外,还有很多其他成员,比如回调函数
  • 事件由事件多路分发器管理,事件处理器则由事件队列管理,事件队列包括多种,比如event_base中的注册事件队列。
  • 事件循环对一个被激活事件(就绪事件)的处理,指的是执行该事件对应的事件处理器中的回调函数。
  1. 调用event_add函数,将事件处理器添加到注册事件队列中,并将该事件处理器对应的事件添加到事件多路分发器中。even_add函数相当于Reactor中的register_handler方法。
  2. 调用event_base_dispatch函数来执行事件循环
  3. 事件循环结束后,使用*_free系列释放系统资源

(推荐课程:Linux就该这么学

源代码组织结构

  • github地址:https://github.com/libevent/libevent
  • 头文件目录include/event2。该目录是自Libevent主板本升级到2.0之后引入的,是提供给应用程序使用的,比如event.h头文件是核心函数,http.h头文件提供HTTP协议相关服务,rpc.h头文件提供远程过程调用支持。
  • 源码根目录下的头文件。这些头文件分为两类:
  • 一类是对include/event2目录下的部分头文件的包装
  • 另外一类是供Libevent内部使用的辅助性头文件,它们的文件名都具有*-internal.h的形式。
  • 通用数据目录compat/sys。该目录下仅有一个文件—-queue.h。它封装了跨平台的基础数据结构,包括单向链表、双向链表、队列、尾队列和循环队列。
  • sample目录。提供一些示例代码
  • test目录。提供一次额测试代码
  • WIN32-Code。提供Windows平台上的一些专用代码。
  • event.c文件。该文件时间Libevent的整体框架,主要是eventevent_base两个结构体的相关操作。
  • debpoll.ckqueue.cevport.cselect.cwin32select.cpoll.cepoll.c文件。它们分别封装了如下I/O复用机制:/dev/pollkqueueevent portsPOSIX selectWindows selectpollepoll。这些文件的主要内容相似,都是针对结构体eventop所定义的接口函数的具体实现。
  • minheap-internal.h:该文件实现了一个事件堆,以提供对定时事件的支持。
  • signal.c:提供对信号的支持。其内容也是针对结构体eventop所定义的接口函数的具体实现
  • evmap.c文件:它维护句柄(文件描述符或信号)与时间处理器的映射关系
  • event_tagging.c:提供往缓冲区中添加标记数据,比如一个正数,以及从缓冲区中读取标记数据的函数
  • event_iocp文件:提供对Windows IOCP(Input/Output Completion Port,输入输出完成端口)的支持
  • buffer*.c文件:提供对网络I/O缓冲的控制,包括:输入输出数据过滤,传输速率限制,使用SSL(Secure Sockets Layer)协议对应用数据进行保护,以及零拷贝文件传输等。
  • evthread*.c文件:提供对多线程的支持
  • listener.c:封装了对监听socket的操作,包括监听连接和接受连接
  • logs.c文件。它是Libevent的日志文件系统
  • evutil.cevutil_rand.cstrlcpy.carc4random.c文件:提供了一些基本操作,比如生成随机数、获取socket地址信息、读取文件、设置socket属性等
  • evdns.chttp.cevrpc.c地址信息:分别提供了对DNS协议、HTTP协议和RPC(Remote Procddure Call,远程过程调用)协议的支持
  • epoll_sub.c文件,该文件未见使用

在整个源码中,event-internal.hinclude/event2/event_struct.hevent.cevmap.c等4个文件最为重要。它们定义了eventevent_base结构体,并实现了这两个结构体的相关操作。

以上就是关于Linux中高性能I/O框架库Libevent的相关介绍了,希望对大家有所帮助。

如何通过Go语言实现凯撒加密

thbcm阅读(252)

在 2 世纪, 发送机密消息的一个有效方法就是对每个字母进行位移, 使得 'a' 变为 'd''b' 变为 'e' , 依次类推。 这样处理产生的结果看上去就像是一门外语:

L fdph, L vdz, L frqtxhuhg. —— 尤利乌斯·凯撒(Julius Caesar)

正如代码清单 9-6 所示, 使用计算机以数值方式处理字符是非常容易的。

代码清单 9-6 处理单个字符: caesar.go

c := 'a'
c=c+3
fmt.Printf("%c", c)    // 打印出“d”

(推荐课程:Go教程

然而, 代码清单 9-6 展示的方法并不完美, 因为它没有考虑该如何处理字符 'x''y''z' , 所以它无法对 xylophonesyakszebras 这样的单词实施加密。 为了解决这个问题, 最初的凯撒加密法采取了回绕措施, 也就是将 'x' 变为 'a''y' 变为 'b' , 而 'z' 则变为 'c' 。 对于包含 26 个字符的英文字母表, 我们可以通过这段代码实现上述变换:

if c > 'z' {
    c = c - 26
}

凯撒密码的解密方法跟加密方法正好相反, 程序不再是为字符加上 3 而是减去 3 , 并且它还需要在字符过小也就是 c< 'a' 的时候, 将字符加上 26 以实施回绕。 虽然上述的加密方法和解密方法都非常直观, 但由于它们都需要处理字符边界以实现回绕, 因此实际的编码过程将变得相当痛苦。

回转 13 (rotate 13,简称ROT13)是凯撒密码在 20 世纪的一个变体, 该变体跟凯撒密码的唯一区别就在于, 它给字符添加的量是 13 而不是 3 , 并且 ROT13 的加密和解密可以通过同一个方法实现, 这是非常方便的。

现在, 假设搜寻外星智能 (Search for Extra-terrestrial Intelligence, SETI) 的相关机构在外太空扫描外星人通信信息的时候, 发现了包含以下消息的广播:

message := "uv vagreangvbany fcnpr fgngvba"

我们有预感, 这条消息很可能是使用 ROT13 加密的英文文本, 但是在解密这条消息之前, 我们还需要知悉其包含的字符数量, 这条消息包含 30 个字符, 可以通过内置的 len 函数来确定:

fmt.Println(len(message))    // 打印出“30”

注意 Go 拥有少量无须导入语句即可使用的内置函数, len 函数即是其中之一, 它可以测定各种不同类型的值的长度。 例如, 在上面的代码中, len 返回的就是 string 类型的字节长度。 代码清单 9-7 展示的就是外太空消息的解密程序, 你只需要在 Go Playground 运行这段代码, 就会知道外星人在说什么了。

代码清单 9-7 ROT13 消息解密: rot13.go

message := "uv vagreangvbany fcnpr fgngvba"


for i := 0; i < len(message); i++ {    // 迭代字符串中的每一个 ASCII 字符
    c := message[i]
    if c >= 'a' && c <= 'z' {    // 只解密英文字母,至于空格和标点符号则保持不变
        c = c + 13
        if c > 'z' {
            c = c - 26
        }
    }
    fmt.Printf("%c", c)
}

(推荐微课:Go微课

注意, 这段代码中的 ROT13 实现只能处理 ASCII 字符(字节), 它无法处理用西班牙语或者俄语撰写的消息, 不过接下来的一节将会给出这个问题的解决方案。

以上就是关于使用 Go语言实现凯撒加密的相关介绍了,希望对大家有所帮助。

Python版本管理工具与虚拟环境的介绍

thbcm阅读(268)

这里简单的介绍一下Python版本管理工具–pyenv和三个个虚拟环境,分别是virtualenvancondapipenv

版本管理工具–pyenv

这个呢是一个python版本管理的包,你可以通过git直接下载源码,安装方法里面都有https://github.com/pyenv/pyenv git地址,下载下来,然后一步一步跟着做就好了。

简单来看一下几个路径含义

  1. ~/.pyenv/shims/

这里面存放的python命令,是我们在终端输入python时候执行的,我们可以把PATH打印一下,可以看出,这个路径的命令是在最前面的。我们输入python后,pyenv会根据我们设置的去找真正要执行的python命令。

  1. ~/.pyenv/versions/

这个目录里面存放的就是我们安装的python版本。(注意:如果我们安装时候特别慢,不要着急.我们可以把终端打印出来的地址,放在浏览器里,然后下载下来,把下载的文件移动到~/.pyenv/cache/下就可以.刚开始要自己建这个cache目录的)

(推荐教程:python教程

python版本设置有两个命令

  1. pyenv local 名称:在当前目录使用这个python版本
  2. pyenv global 名称:将全局的python版本设置成这个版本

名称我们可以通过pyenv versions来查看,里面会有一个system的名称,这个是你机器原来的python版本,一般情况下我们都用local来针对某一个目录设置python,全局还是用我们系统的。用local我们可以在当前目录下找到一个.python_version的文件.pyenv应该就是读这个文件,知道你在当前目录要用什么版本的python

用过node的可能会发现,其实这个跟nodenvm差不多一样的性质。

虚拟环境–virtualenv

我最开始使用的虚拟环境是virtualenv,这个的用法,网上也有一大堆。简单说一下吧,因为我现在很少用。

安装:pip install virtualenv
创建:virtualenv env名称
进入虚拟环境:source env名称/bin/activate
退出虚拟环境:deactivate

进入环境,你就可以在里面用pip install了。安装的包就是在当前环境中。

虚拟环境–anconda

后来我看还有个anconda的包管理器,也能创建虚拟环境。这个也有很多安装教程。

安装:官网有教程,下载下来运行就可以了
创建:conda create -n env名称 python=2.7
进入:conda activate env名称
退出:conda deactivate

进入环境,你也可以在里面安装包,安装包用的是anconda的命令:conda install 包。这里面可能有一些包找不到,你也可以用pip install来安装。

注意:用pip来安装就有点坑,如果你本地机器上已经安装了这个包了,那pip install就安装不了,如果你pip install安装的包跟你本机的版本不一样,他会卸载了本机的包,然后重新在你的conda环境中新安装一个包。这样你本机就没有这个包了。对于一个新机器来说,可能比较好吧,毕竟以后运行项目都在虚拟环境,但是对于一些有在本机环境运行项目的人来说,就不那么友好,有可能你用着anconda,莫名其妙的本机就缺少包。当然你也可以再安装。

虚拟环境–pipenv

后来,最近我又发现了一个pipenv,这个感觉比较好。

安装:pip install pipenv
创建:pipenv install --python=2.7
进入:进入目录,pipenv shell
退出:deactivate

创建一个虚拟环境,存放在默认目录下,我的默认目录是~.local/share/下面,然后会在当前目录创建一个Pipfile的文件。里面记录着你安装的包。安装包用pipenv install ,安装的包,都会在Pipfile里面记录着,如果你当前目录已经有Pipfile,你pipenv install,他就会创建一个跟当前目录关联的虚拟环境,然后安装Pipfile中的包。里面可以设置下载包的源。以提高下载速度。安装完,会生成一个Pipfile.lock的文件。里面记录着真实的下载的包的一些信息,当项目迁移的时候,把这几个目录一起,不管在哪里,运行的环境都是一样的。这也是我比较喜欢的一点,有点像nodepackage.json文件的功能。

(推荐微课:python3基础微课

以上就是关于Python版本管理工具和虚拟环境的相关介绍了,希望对大家有所帮助。

给大数据分析实习生的面试经验题库

thbcm阅读(196)

大数据分析是一个有吸引力的领域。这是有利可图的,您有机会从事有趣的项目,而且您总是在学习新事物。因此,进入大数据分析领域极具竞争力。开始大数据分析事业的最佳方法之一是通过大数据分析实习。

在大数据分析实习生面试题库中,我们将研究所需的一般知识水平,典型面试过程的组成部分以及一些面试问题示例。注意,强调“通用”一词是因为具体情况因公司而异。

大数据分析实习面试会有什么期望?

大数据分析实习面试和专职大数据分析师之间的最大区别在于,通常不会期望您了解有关机器学习或深度学习概念的极其具体的细节。

但是,您将期望拥有能够在其上进行构建的基本构建块-包括PythonRSQL,统计和概率基础 以及 基本的机器学习概念。

Python和R

您应该具有脚本语言(最好是Python或R)的编程经验。如果您是Python程序员,则还应该对流行的库(如Scikit-learn 和 Pandas)有基本的了解 。

(推荐教程:python教程

您应该了解的内容: 您应该知道如何编写基本功能,并对各种数据结构及其用途有基本的了解。您还应该了解Scikit-learn的基本(但仍必不可少)功能,例如test_train_splitStandardScaler。对于Pandas,您应该像使用SQL编写查询那样舒适地操作DataFrame

例如,您可能需要构建一个简单的机器学习模型来预测产品的销售数量。在这种情况下,如果您是Python用户,那么了解Scikit-Learn库将非常有用,因为它已经提供了许多预构建的函数,例如上面提到的那些函数。

如何准备: 尝试在Kaggle上进行大数据分析项目或在Interview Query上进行实地考察,以了解您可能需要完成哪些项目。

为了更好地了解Scikit-Learn,最好使用它构建一个简单的机器学习模型,或者逐步完成其他人已经完成的一些大数据分析项目。

(推荐微课:python3基础微课

最后,尝试在Interview Query上练习Python问题,以了解他们可能会问您什么。

SQL

不会期望您在关系数据库方面有太多的经验,但是至少,您应该了解SQL的工作方式。 如果您正在争取大数据分析师的实习机会,那么您很可能会在拥有大量数据的公司工作。您将需要亲自浏览这些数据来解决问题。

(推荐课程:SQL教程)

您应该了解的内容: 您应该能够编写基本查询,并且应该知道如何使用SQL查询来操纵数据。对于公司而言,将SQL纳入其实际案例研究中非常普遍,因此,您必须非常了解SQL

示例问题

编写一个SQL查询以从Employee 表中获取第二高的薪水 。例如,给定下面的Employee表,查询应返回 200 作为第二高的薪水。如果没有第二高的薪水,则查询应返回 null

  + —- + ———- +

  | ID | 薪金|

  + —- + ———- +

  | 1 | 100 |

  | 2 | 200 |

  | 3 | 300 |

  + —- + ———- +

如何准备: 模式为学习基本SQL提供了很好的资源,可以在这里找到。此外,您还可以在线找到大量的SQL练习问题和练习案例研究。

(推荐微课:SQL微课)

统计与概率

您应该对基本统计数据和概率有所了解 。这些概念是大多数机器学习和大数据分析概念的基础。同样,许多要求大数据分析职位的面试问题都与统计有关。

您应该了解的内容: 您应该对基本概念有扎实的理解,包括但不限于概率基础,概率分布,估计和假设检验。统计数据的一个非常普遍的应用是条件概率,例如,假设客户购买了产品C,那么购买该产品B的概率是多少?

如何准备: 如果您对这些概念感到陌生,则可以利用许多免费资源,例如Khan AcademyGeorgia Institute of Technology

机器学习概念

虽然不希望您成为专家,但是您应该对基本的机器学习模型和概念有很好的了解 。如果职位描述表明您将要构建模型,则尤其如此。

您应该了解的内容: 这包括但不限于线性回归,支持向量机和聚类之类的概念。理想情况下,您应该对这些概念有基本的了解,并了解何时适合使用各种机器学习方法。

  例如,您可能需要对产品的价格点实施线性回归以确定销售数量。话虽如此,您将不需要生产或部署机器学习模型作为实习生。

领域知识

您应该对 所申请的领域具有 领域知识(如果没有,则应该学习)。

例如,如果您要申请市场营销部门的大数据分析职位,那么了解不同的营销渠道(例如社交媒体,会员,电视)以及核心指标(例如LTV, CAC)。

大数据分析实习面试流程

同样,面试过程最终取决于您所申请的公司。但是一般来说,大多数(如果不是全部)公司在面试过程中都有一些一般步骤,我将在下面进行解释。

作为实习生, 最糟糕的事情是不对公司的工作进行研究 ,这是文化使命和价值观。

初步筛选

通常,由公司的招聘人员或招聘经理进行初步筛选(通常是电话筛选)。这样做的目的是为了使受访者更好地了解其角色,并使访问者更好地了解受访者。

您应该期望他们询问您对这个职位和公司的兴趣,为什么认为自己很合适,以及与您过去的经历有关的问题。在极少数情况下,您可能还会被问到一个或两个简单的技术问题。

面试官只是在确保您对公司真正感兴趣,您是一个很好的沟通者,并且没有提出任何危险信号。

带回家的情况

对于现在的许多大数据分析实习,公司将要求您完成一项实战挑战。这意味着他们会给您一定的时间来完成他们给您的案例研究,这通常反映出您在实际角色中会遇到的问题。

这样做是为了了解您如何解决问题(即思考过程),以及您是否具有完成问题所需的基本知识。案例的示例包括 清理数据集 并 建立机器学习模型以做出给定的预测或查询数据集并分析数据或两者结合。

现场采访

最后是现场采访,可以包括一轮到多达六轮的采访。这些面试由行为和技术面试问题组成。您可能还需要现场完成一轮案件。

当他们试图确保您对成功担任该角色所需的基本知识有深刻的了解时,他们还将评估您的行为动机,并最终评估您是否适合团队或不。确保您处于最佳行为状态,但不要忘记做自己!

面试问题

以下是您希望了解的一些面试问题的几个示例:

  1)什么是p值?

  2)什么是正则化,它试图解决什么问题?

  3)您如何将年龄和收入之间的关系转换成线性模型?

  4)如果您有两个相等重量的骰子,总和为4的概率是多少?

  5)在整理和清理数据集时需要采取哪些步骤?

  6)什么是交叉验证,为什么有必要?

  7)举例说明在确定机器学习模型有效性时,准确性不是最佳指标。

  8)INNEROUTER JOIN有什么区别?

以上就是关于大数据大数据分析实习生的面试经验题库的相关介绍了,希望对大家有所帮助。

10条一行代码带你领略Python的魅力

thbcm阅读(290)

在了解Python并用它写了一次代码后,它的简单性,优秀的可读性和好用的一行代码深深的吸引了我。接下来,我会给大家介绍一些一行代码,或许对你的Python项目有所帮助。

1.交换两个变量

# a = 1; b = 2
a, b = b, a
# print(a,b) >> 2 1

让我们从一个经典的开始:通过简单地交换赋值位置来交换变量的值——在我看来,这是最直观的方法。不需要使用临时变量。它甚至适用于两个以上的变量。

2.多个变量赋值

a, b, *c = [1,2,3,4,5]
# print(a,b,c) >> 1 2 [3, 4, 5]

交换变量实际上是python能够一次分配多个变量的一种特殊情况。在这里,您可以使用它将列表元素分配给给定的变量,这也称为解表。 * 将再次打包剩下的值,这将导致c的子列表。它甚至可以用于*的其他位置(例如列表的开始或中间部分)。

3.对列表每隔两个元素求和

# a = [1,2,3,4,5,6]
s = sum(a[1::2])
# print(s) >> 12

这里不需要特殊的reduce函数,sum只是添加每个给定迭代的项。这里使用扩展的切片语法[::]来返回第二个元素。你可以将它读为[start: stop: step],所以[1::2]翻译为从索引 1 的元素开始(第二个元素),直到列表结束(第二个参数没有给出参数),并且总是采取两步。

(推荐教程:python教程

4.删除列表多个元素

# a = [1,2,3,4,5]
del a[::2]
# print(a) >> [2, 4]

扩展的切片语法也可以用来一次删除多个列表元素。

5.将文件读入行数组

c = [line.strip() for line in open('file.txt')]
# print(c) >> ['test1', 'test2', 'test3', 'test4']

使用python内联for循环,您可以轻松地将文件读入行数组中。需要使用strip()来删除后面的断线。如果你想保留它们或者它们对你来说不重要,你可以用更短的一行字:

c = list(open('file.txt'))
# print(c) >> ['test1\n', 'test2\n', 'test3\n', 'test4\n']

Python中读取文件真的很简单。附注:如果愿意,还可以使用readlines()方法。

6.将字符串写入文件

with open('file.txt', 'a') as f: f.write('hello world')
# print(list(open('file.txt'))) >> ['test1\n', 'test2\n', 'test3\n', 'test4\n', 'hello world']

With语句的帮助下,您可以直接将内容写入文件。确保使用正确的模式打开文件(这里“a”表示附加内容)。

7.创建列表

l = [('Hi ' + x) for x in ['Alice', 'Bob', 'Pete']]
# print(l) >> ['Hi Alice', 'Hi Bob', 'Hi Pete']

可以使用内联for循环从其他列表动态创建列表。您可以直接修改值,就像本例中的字符串连接一样。

8.列表映射

l = list(map(int, ['1', '2', '3']))
# print(l) >> [1, 2, 3]

还可以使用Pythons map()函数将每个列表元素强制转换为另一种类型。

(推荐微课:python3基础微课

9.集合创建

squares = { x**2 for x in range(6) if x < 4 }
# print(squares) >> {0, 1, 4, 9}

集合也是一样的。除了内联for循环之外,您甚至可以直接添加条件!

10.回文检查

# phrase = 'deleveled'
isPalindrome = phrase == phrase[::-1]
# print(isPalindrome) >> true

回文是一系列向前和向后读取相同的字符。如果给定字符串是回文,通常需要一些循环和条件来检查。在Python中,你只需要比较字符串和它的反向字符串。除了使用切片操作符[::-1]之外,您还可以使用reverse()函数来反转字符串。

以上就是关于Python一行代码的一些知识了,希望对大家有所帮助。

英文原文:dev.to/devmount/10-awesome-pythonic-one-liners-explained-3doc

Nodejs 与 Golang的对比,哪个更适合Web 开发

thbcm阅读(204)

互联网技术更新换代很快,全世界的开发人员都会关注技术趋势,这些技术最终将帮助他们在非常短的时间内设计出优秀的软件。很多时候,程序员很难根据客户的要求选择最佳语言来创建移动应用程序。Web 开发是发展最快的领域之一。每个行业都希望通过在线渠道扩展业务,以赢得更多的客户并加强营销。选择一流的技术(例如 Golang 应用程序开发或 NodeJS)变得至关重要。但是,哪个能为您的业务创造奇迹?

(推荐教程:Node入门

在当今的数字世界中,企业可以通过改进网站的不同部分(例如速度,外观、内容等)来吸引更多的客户。所有这些因素在吸引访问者和客户方面都起着重要的作用。功能丰富的编程语言的选择取决于两个重要因素,前端软件开发和后端软件开发。此外,移动应用程序的顺利运行还取决于用户的满意度。在本文,我们将讨论 NodeJSGolang 这两种广为人知的语言,开发人员可以选择这两种语言开发出色的软件和移动应用程序。

我们来谈谈 NodeJS

NodejsChrome V8平台上的一门功能强大的 javascript 语言运行时。它是一门很棒的开源语言,它使开发人员和编码人员(指前端人员)可以创建可在所有平台(Windows,ios,Android 等)上平稳运行的Web应用程序。另一个最佳方面是JS应用程序开发是一种跨平台的网页设计工具,可帮助简化编码人员的任务。集成到节点中的各种 javascript模块,允许程序员在基于Web的应用程序中添加独特的功能。工程师会发现,在服务器以及客户端部分上编写不同的代码很容易。最终减少了了解其他语言概念所花费的时间。

(推荐教程:Node.js教程

工程师可以使用这种有前途的语言在网络上进行软件开发。遇到棘手问题时,可以在社区中找到经验丰富的程序员大佬,在他们的帮助下轻松解决问题。它在I/O模型上运行,可轻松用于各种特定的Web应用程序。

  • 开发人员可以根据Web应用程序项目来个性化Node.js中的功能。
  • 作为一门服务端语言,可以有效地用于后端和前端,在技术领域是家喻户晓。
  • Node.js使开发人员可以轻松地多次使用代码。
  • Node.js中的 V8 技术使将编码合并到机器中变得更加简单。
  • Node.js还可以帮助开发人员在Web开发中缓存每个模块。

(推荐微课:Node.js微课

我们来谈谈 Golang

它由 Google开发,是一流的开源编程语言,可轻松创建 Web应用程序。使用Golang编写的Web应用程序可以在各种平台上稳定运行。对于大型的基于Web 的应用程序,Golang 是最佳选择。该语言以其管理Web应用程序的现有编码任务的能力而闻名。这也是开发人员即时学习的一种非常容易的语言。Golang使用的是C族的语法。

开发人员更喜欢 Golang应用程序开发,因为它可以在云上开发出大量 Web 应用程序。反过来,这减少了数据被盗的风险,因为信息只能由组织中的相关人员访问。而且,上级人员可以通过世界任何地方来访问信息。它使用 C 语言来简化编码, 开发人员都非常容易学习这种语言。Golang 的另一个最好的部分是垃圾收集。使用 Golang 设计的网络应用程序运行速度更快。通过 Golang 编写的 Web 程序也是非常的安全可靠。

  • Golang 允许开发人员轻松编写代码。此外,更改代码也非常简单。
  • Golang 的垃圾收集功能有助于降低工具的延迟率,以便开发可以通过简单的方法添加独特的算法。
  • Golang 创建的 Web 应用程序也可以扩展,因为它可以使用 goroutines 有效地处理众多任务。
  • Go 编译器集成,可以帮助开发人员快速编译出适合各种操作系统运行的二进制文件。

(推荐课程:Go教程

Nodejs 和 Golang 的区别

1. 语言的性能

如果 Web 应用程序执行效率高,则可以在不同平台上快速加载。这对于数字营销非常重要,因为网站的速度会吸引更多的访客。最终,它可以提高潜在用户的满意度,并增加口碑营销。Go 应用程序最终会被编译为机器语言。

Go 还包括垃圾收集器,可以轻松处理内存分配并释放不使用的内存。此过程有助于减少网络攻击,因为它可以轻松处理内存。当需要为网站访客开发一个 Web 应用程序时,Go 是最佳选择。

Nodejs 一般都用来做后端开发。它使用 Javascript 语言进行开发,不会阻碍 Web 应用程序的开发过程。开发可以在后端执行操作,前端部分完全不受影响。而且,V8 引擎是所有可用 JS 平台中最快的,这反过来又使 Nodejs 成为有前途的工具。另外,Nodejs 编写的代码可以在开发Web 应用程序中多次使用。

2. Web应用程序开发过程中的错误管理

关于 Nodejs,在 Web 应用程序开发过程中出现的问题可以一目了然。但是,对于 Golang 应用程序开发过程中出现的错误不会很清楚的分开展现。这最终会给后面的开发人员带来麻烦。GoogleGo Team 成员总是计划在短时间内添加独特的功能以吸引开发人员。因此,Nodejs 应用程序开发在这方面是最好的。

3. 在开发可扩展方面

请牢记在运行 Web 应用程序时 Golang 的可伸缩性。使用 Goroutine 有助于 Golang 应用开发。可以轻松,高效地执行许多任务。反过来,这又可以快速改善 Web 应用程序的功能。

对于 NodejsWeb 应用程序的设计方式非常不同。它在单个平台上运行,但是整个部分按正确的顺序进行。JS 中的所有编码过程都借助于 Node 中的回调功能完成。这就是为什么 Nodejs 中没有可伸缩性的原因。在这方面,Go 编程语言是不错的选择。

4. 开发中的技术

正确的技术和独特功能的使用使开发人员能够快速开发出超赞的网站。Nodejs 涉及各种技术和框架的集成,这使得一些有定制需求的 Web 应用程序创建变得更简单。此外,JavaScript 语言具有广泛的社区,来支持Web 应用程序项目设计。

另一方面,Go 编程语言受到程序员和工程师的高度评价,但仍处于早期发展阶段。尽管有多种可用的框架和技术,但不如 Nodejs 丰富。因此,在这种情况下,Nodejs 应用程序开发是赢家。

5. 两种编程语言都需要的学习时间

知道 JavaScript 概念的开发人员会发现,Nodejs 语言是能最快速度掌握的语言。如果工程师可以在更少的时间内学习该语言,那么他们将能够更快地开始开发 Web 应用程序。自古以来,IT 部门中都会存在 JavaScript 的使用。这就是为什么 Nodejs 能快速上手的原因。同时学习渠道以及资源丰富。Nodejs 中的新手可以向经验丰富的老手请教问题,来快速解决 Web 应用程序开发时遇到的问题。网上也有很多关于 NodeJs Web 应用程序开发的课程。

(推荐课程:Go Web编程

Golang 应用程序开发是 IT 领域的新概念。它适用于 Google 制定的另一套规则。通过 Golang 设计 Web 应用程序包括不同的过程,界面等。它由 Google 开发,其中包含许多其他技术,这些技术对于全球的程序员和编码人员而言都是相对较新的技术。Go 语言的另一个问题是,它仅用于 Web 应用程序后端设计。这样就不得不再招一些前端Web 应用程序开发人员。这将花费更多时间。作为老板,您的主要目标是利用您的时间来扩展业务并增加营业额和收入。您将无法参与Web 应用程序设计任务。而且 Golang 社区也没有 Nodejs 社区那么广泛。开发 Golang 应用程序需要开发对 Golang 有深入的研究。当通过 Golang 设计 Web 应用程序出现问题时,想要快速找到精通的人帮忙解决比较有难度。反过来,这是一个耗时的过程,对于有 deadline 的项目会比较有挑战。因此,Nodejs 非常适合Web应用程序开发的公司。吸引人的是,它能提供众多功能。公司充分利用 Nodejs 应用开发的潜力,为全球不同客户创建出色的 Web 应用。

6. 两种语言的社区

嗯,这两种语言都是完全开源的。每个都有自己的基础社区,以帮助新的开发人员和编码人员。但是,与 Golang 应用程序开发相比,Nodejs 平台拥有更广泛的社区。NodejsJavaScript 上起作用,JavaScriptWeb 应用程序开发中最常用的语言。而 Golang 的社区比 Nodejs 小得多。由 Google 开发的 Golang,开发人员举办许多活动,探讨对这种编程语言进行更新、实践和改进。

(推荐微课:Go微课

结束语

所以,现在您对这两种编程语言都有了深入的了解。现在可以得出结论,选择哪个编程语言是取决于当前的项目特点。如果 Web 开发人员是技术领域的新手,那么 Nodejs 将是理想的选择。但是,Golang 是新编程语言,当然学习需要很多时间。同样,这取决于工程师根据他们的要求来选择。

以上就是关于NodejsGolang 的比较,希望对大家有所帮助。

Github标星14K的一款国产Java工具类库

thbcm阅读(222)

最近在Github上面看到一款小而全的Java工具类库,已经接近14K Star了,想来这肯定是一款优秀的软件,现在给大家介绍一下。

![Hutool](https://atts.w3cschool.cn/attachments/image/20200814/1597384513592387.jpg “Hutool”)

Hutool 是什么

Hutool 是一个Java工具包类库,它可以对文件、流、加密解密、转码、正则、线程、XMLJDK方法进行封装,组成各种Utils工具类。

Hutool 即是Hu(谐音“糊涂”) + tool,前者致敬作者 “前任公司”,后者为工具之意,谐音“糊涂”,寓意追求“万事都作糊涂观,无所谓失,无所谓得”的境界。

(推荐教程:Java教程

Hutool如何改变我们的coding方式

Hutool的目标是使用一个工具方法代替一段复杂代码,从而最大限度的避免“复制粘贴”代码的问题,彻底改变我们写代码的方式。

以计算MD5为例:

【以前】打开搜索引擎 -> 搜“Java MD5加密” -> 打开某篇博客-> 复制粘贴 -> 改改好用

【现在】引入Hutool -> SecureUtil.md5() Hutool的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug

是不是很香,再也不要到处找这些工具类代码了

包含组件

一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XMLJDK方法进行封装,组成各种Util工具类,同时提供以下组件:

![包含组件](https://atts.w3cschool.cn/attachments/image/20200814/1597384589447484.jpg “包含组件”)

安装

Maven 在项目的 pom.xmldependencies中加入以下内容:

![安装](https://atts.w3cschool.cn/attachments/image/20200814/1597384660664063.jpg “安装”)

Gradle:

compile ‘cn.hutool:hutool-all:5.3.10’

这个类库还是咱国人程序员们开源的。

(推荐微课:Java微课

Hutool 也欢迎任何人为Hutool添砖加瓦,贡献代码,不过作者是一个强迫症患者,为了照顾病人,需要提交的pr(pull request)符合一些规范。

以上就是关于Github标星14K的一款国产Java工具类库:Hutool的相关介绍了,希望对大家有所帮助。

一文看懂React17新特性——启发式更新算法

thbcm阅读(235)

三天前,React团队发布了React17的第一个RC版本,这个版本最大的特性就是“无新特性”。

那么,从v16v17这一年多时间React团队究竟在做什么?

遥想从v15v16React团队花了两年时间将源码架构中的Stack Reconciler重构为Fiber Reconciler,事情一定没有这么简单。

事实上,这次版本更迭确实有“新特性” —— 替换了内部使用的启发式更新算法。

只不过这个特性对开发者是无感知的。

本文接下来将讲述如下内容:

  • 起源:为什么会出现启发式更新算法?
  • 现状:React16的启发式更新算法及他的不足
  • 未来:React17的启发式更新算法

为什么会出现启发式更新算法

框架的运行性能是框架设计者在设计框架时需要重点关注的点。

Vue使用模版语法,可以在编译时对确定的模版作出优化。

ReactJS写法太过灵活,使他在编译时优化方面先天不足。

所以,React的优化主要在运行时。

React15的痛点

在运行时优化方面,React一直在努力。

比如,React15实现了batchedUpdates(批量更新)。

即同一事件回调函数上下文中的多次setState只会触发一次更新。

但是,如果单次更新就很耗时,页面还是会卡顿(这在一个维护时间很长的大应用中是很常见的)。

这是因为React15的更新流程是同步执行的,一旦开始更新直到页面渲染前都不能中断。

为了解决同步更新长时间占用线程导致页面卡顿的问题,也为了探索运行时优化的更多可能,React开始重构并一直持续至今。

重构的目标是实现Concurrent Mode(并发模式)。

(推荐教程:React教程

Concurrent Mode

Concurrent Mode的目的是实现一套可中断/恢复的更新机制。

其由两部分组成:

  • 一套协程架构
  • 基于协程架构的启发式更新算法

其中,协程架构就是React16中实现的Fiber Reconciler

我们可以将Fiber Reconciler理解为React自己实现的Generator

Fiber Reconciler从理念到源码的详细介绍见这里

协程架构使更新可以在需要的时机被中断,这样浏览器就有时间完成样式布局与样式绘制,减少卡顿(掉帧)的出现。

当浏览器进入下一次事件循环,协程架构可以恢复中断或者抛弃之前的更新,重新开始新的更新流程。

启发式更新算法就是控制协程架构工作方式的算法。

React16的启发式更新算法

启发式更新算法的启发式指什么呢?

启发式指不通过显式的指派,而是通过优先级调度更新。

其中优先级来源于人机交互的研究成果。

比如:

人机交互的研究成果表明:

  • 当用户在输入框输入内容时,希望输入的内容能实时响应在输入框
  • 当异步请求数据后,即使等待一会儿再显示内容,用户也是可以接受的

基于此,在React16中

输入框输入内容触发的更新优先级 > 请求数据返回后触发更新优先级

算法实现 在React16、17中,在组件内执行this.setState后会在该组件对应的fiber节点内产生一种链表数据结构update

其中,update.expirationTimes为类似时间戳的字段,表示优先级。

expirationTimes从字面意义理解为过期时间。

该值离当前时间越接近,该update 优先级越高。

update.expirationTimes超过当前时间,则代表该update过期,优先级变为最高(即同步)。

一棵fiber树的多个fiber节点可能存在多个update

每次Fiber Reconciler调度更新时,会在所有fiber节点的所有update.expirationTimes中选择一个expirationTimes(一般选择最大的),作为本次更新的优先级。

并从根fiber节点开始向下构建新的fiber树。

构建过程中如果某个fiber节点包含update,且

update.expirationTimes >= expirationTimes

则该update对应的state变化会体现在本次更新中。

可以理解为:每次更新,都会选定一个优先级(expirationTimes),最终页面会渲染为该优先级对应update的快照。

举个例子,我们有如图所示fiber树,当前还没有更新产生,所以没有构建中的fiber树。

当在 C 创建一个低优先级update,调度更新,本次更新选择的优先级为低优先级。

开始构建新的fiber树(图右侧)。

此时,我们在 D 创建一个高优先级update

这会中断进行中的低优先级更新,重新开始以高优先级生成一棵fiber树。

由于之前的更新被中断,还没有任何渲染操作,此时视图中(左图)还没有任何变化。

本次更新选定的优先级为高优先级,C 的update(低优先级)会被跳过。

更新完成后新的fiber树会被渲染到视图中。

由于 C 被跳过,所以不会在视图(左图)中体现。

接下来我们在 E 触发一次高优先级update

C 虽然包含低优先级update,但随着时间的推移,他的expirationTimes已经过期,变为高优先级。

所以本次更新会有 C E 两个fiber节点产生变化。

最终完成更新后,视图如下:

算法缺陷

如果只考虑中断/继续这样的 CPU 操作,以expirationTimes大小作为衡量优先级依据的模型可以很好工作。

但是expirationTimes模型不能满足 IO 操作(Suspense)。

在该模型下,高优先级 IO 任务(Suspense)会中断低优先级 CPU 任务。

还记得么,每次更新,都是以某一优先级作为整棵树的优先级更新标准,而不仅仅是某一组件,即使更新的源头(update)确实是某个组件产生的。

expirationTimes模型只能区分是否>=expirationTimes这种情况。

为了拓展Concurrent Mode能力边界,需要一种更细粒度的启发式优先级更新算法。

(推荐教程:React入门实例教程

React17启发式更新算法

最理想的模型是:可以指定任意几个优先级,更新会以这些优先级对应update生成页面快照。

但是现有架构下,该方案实现上有瓶颈。

妥协之下,React17的解决方案是:指定一个连续的优先级区间,每次更新都会以区间内包含的优先级生成对应页面快照。

这种优先级区间模型被称为lanes(车道模型)。

具体做法是:使用一个31位的二进制代表31种可能性。

  • 其中每个bit被称为一个lane(车道),代表优先级
  • 某几个lane组成的二进制数被称为一个lanes,代表一批优先级

可以从源码中看到,从蓝线一路划下去,每个bit都对应一个lanelanes

update产生,会根据React16同样的启发式方式,获得如下优先级的一种:

export const SyncLanePriority: LanePriority = 17; export const SyncBatchedLanePriority: LanePriority = 16; export const InputDiscreteLanePriority: LanePriority = 14; export const InputContinuousLanePriority: LanePriority = 12; export const DefaultLanePriority: LanePriority = 10; export const TransitionShortLanePriority: LanePriority = 8; export const TransitionLongLanePriority: LanePriority = 6;

其中值越高,优先级越大。

比如:

  • 点击事件回调中触发this.setState产生的update会获得InputDiscreteLanePriority
  • 同步的update会获得SyncLanePriority

接下来,update会以priority为线索寻找没被占用的lane

如果当前fiber树已经存在更新且更新的lanes包含了该lane,则update需要寻找其他lane

比如,InputDiscreteLanePriority对应的lanesInputDiscreteLanes

// 第4、5位为1 const InputDiscreteLanes: Lanes = 0b0000000000000000000000000011000;

lanes包含第4、5位 2 个 bit位。

如果其中

// 第五位为1 0b0000000000000000000000000010000

第五位的lane已经被占用,则该update可以尝试占有后一个,即

// 第四位为1 0b0000000000000000000000000001000

如果InputDiscreteLanes的两个lane都被占用,则该update的优先级会下降到InputContinuousLanePriority并继续寻找空余的lane

这个过程就像:购物中心每一层(不同优先级)都有一个露天停车场(lanes),停车场有多个车位(lane)。

我们先开车到顶楼找车位(lane),如果没有车位就下一楼继续找。

直到找到空余车位。

由于lanes可以包含多个lane,可以很方便的区分 IO 操作(Suspense)与 CPU 操作。

当构建fiber树进入构建Suspense子树时,会将Suspenselane插入本次更新选定的lanes中。

当构建离开Suspense子树时,会将Suspense lane从本次更新的lanes中移除。

(推荐微课:React微课

总结

React16expirationTimes模型只能区分是否>=expirationTimes决定节点是否更新。

React17lanes模型可以选定一个更新区间,并且动态的向区间中增减优先级,可以处理更细粒度的更新。

以上就是关于React17的新特性–启发式更新算法的相关介绍了,希望对大家有所帮助。

Spring Boot 2.x基础教程:使用集中式缓存Redis

thbcm阅读(223)

在本文中我们来学习一下,如何在Spring Boot的缓存支持中使用Redis实现数据缓存。

(推荐教程:Spring Boot 那些事)

动手试试

User实体的定义

@Entity @Data @NoArgsConstructor public class User implements Serializable {


    @Id
    @GeneratedValue
    private Long id;


    private String name;
    private Integer age;


    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}

User实体的数据访问实现(涵盖了缓存注解)

@CacheConfig(cacheNames = “users”) public interface UserRepository extends JpaRepository<User, Long> {


    @Cacheable
    User findByName(String name);


}

(推荐课程:Spring教程

下面开始改造这个项目:

第一步pom.xml中增加相关依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>


<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

Spring Boot 1.x的早期版本中,该依赖的名称为spring-boot-starter-redis,所以在Spring Boot 1.x基础教程中与这里不同。

第二步:配置文件中增加配置信息,以本地运行为例,比如:

spring.redis.host=localhost spring.redis.port=6379 spring.redis.lettuce.pool.max-idle=8 spring.redis.lettuce.pool.max-active=8 spring.redis.lettuce.pool.max-wait=-1ms spring.redis.lettuce.pool.min-idle=0 spring.redis.lettuce.shutdown-timeout=100ms

关于连接池的配置,需要注意:

Redis的连接池配置在 1.x 版本中前缀为spring.redis.poolSpring Boot 2.x有所不同。在 1.x 版本中采用jedis作为连接池,而在 2.x 版本中采用了lettuce作为连接池以上配置均为默认值,实际上生产需进一步根据部署情况与业务要求做适当修改.

再来试试单元测试:

@Slf4j @RunWith(SpringRunner.class) @SpringBootTest public class Chapter54ApplicationTests {


    @Autowired
    private UserRepository userRepository;


    @Autowired
    private CacheManager cacheManager;


    @Test
    public void test() throws Exception {
        System.out.println("CacheManager type : " + cacheManager.getClass());


        // 创建1条记录
        userRepository.save(new User("AAA", 10));


        User u1 = userRepository.findByName("AAA");
        System.out.println("第一次查询:" + u1.getAge());


        User u2 = userRepository.findByName("AAA");
        System.out.println("第二次查询:" + u2.getAge());
    }


}

执行测试输出可以得到:

CacheManager type : class org.springframework.data.redis.cache.RedisCacheManager Hibernate: select next_val as id_val from hibernate_sequence for update Hibernate: update hibernate_sequence set next_val= ? where nextval=? Hibernate: insert into user (age, name, id) values (?, ?, ?) 2020-08-12 16:25:26.954 INFO 68282 — [ main] io.lettuce.core.EpollProvider : Starting without optional epoll library 2020-08-12 16:25:26.955 INFO 68282 — [ main] io.lettuce.core.KqueueProvider : Starting without optional kqueue library Hibernate: select user0.id as id10, user0_.age as age20, user0_.name as name30 from user user0 where user0.name=? 第一次查询:10 第二次查询:10

(推荐微课:Spring微课)

可以看到:

  1. 第一行输出的CacheManager typeorg.springframework.data.redis.cache.RedisCacheManager,而不是上一篇中的EhCacheCacheManager
  2. 第二次查询的时候,没有输出SQL语句,所以是走的缓存获取

以上就是关于Spring Boot 2.x基础教程:使用集中式缓存Redis的相关介绍了,希望对大家有所帮助。

联系我们