开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

文本表示的应用与评价

发表于 2017-11-13

前言:本文是对CMU CS 11-747(Neural Networks for NLP)课程中Using/Evaluating Sentence Representations章节内容的一个梳理。本文第一部分将对前几次课程中提到的文本表示方法进行一个概括。第二部分介绍五种文本表示的应用,分别是句子分类、释义识别、语义相似度、文本蕴涵和信息检索。本文将分别介绍这五种应用的任务及评价指标、常见数据集及其产生的方法、以及使用的模型。第三部分将做一个简要总结并分享一些相关问答。

本文作者:李思晴,2014级本科生,目前研究方向为深度学习、推荐系统,来自中国人民大学大数据管理与分析方法研究北京市重点实验室。

一、文本表示方法的简要概括

本文大致介绍两类文本表示方法。一类是将一个句子表示为一个向量,例如向量空间模型(VSM),也称词袋模型(BOW)。定义一个与字典长度相同的向量,向量中每个位置对应字典中相应位置的单词,句子中出现的单词,就在向量的对应位置填入相应权值,这样就可以用一个向量来表示一个句子。另一类是将一个句子表示为多个向量的序列,例如利用word embedding,将句子中每个单词都转化为一个向量,这样,一个具有n个单词的句子就可以表示为n个向量的序列。


二、文本表示的应用与评价方法

1、句子分类(Sentence Classification)

1.1 任务:根据主题或者情感语义等特性对句子进行分类。

1.2 模型:CNN在NLP中的一大应用就是句子分类。

传统的句子分类是使用SVM和Naive Bayes,它们选择的文本表示方法大多是词袋模型,会忽略词序信息。CNN用的是word-embedding来表示句子,可以保留词的序列特征。模型如下图所示:


首先将句子中的单词向量化作为输入,然后用CNN抽取句子的特征,可以得到对应的一个分数,再通过softmax函数将分数映射成概率。本专栏上一篇文章《从CNN视角看在自然语言处理上的应用》中有对CNN更详细的介绍,在此就不过多赘述了。

1.3 数据集:Stanford Sentiment Treebank (Socher et al. 2013)

下图为一个例子:


此数据集特殊的地方在于,句子中的每个单词都标有一个情感值,介于1到25之间,1表示最负面,25表示最正面。可以用此数据集来进行句子的情感分类。

2、释义识别(Paraphrase Identification)

2.1 任务:识别两个句子意思是否相同。

2.2 数据集:Microsoft Research Paraphrase Corpus (Dolan and Brockett 2005)

生成步骤:

(1)爬取大量新闻语料

(2)用启发式方法或分类器自动判别语句是否意思相同

(3)由评价者来判定两个句子是否真的相似

由于该数据集需要人工评测,所以数据量比较小但十分高质量。

2.3 模型:将两个句子的向量表示输入分类器,输出意思是否相同。


这里介绍一个可以用来做释义识别的句子表示模型:Skip-thought Vectors(Kiros et al. 2015) NIPS

这个模型训练时的输入是文档中的第i个句子,输出是第i-1个句子和第i+1个句子,例如下图:


这三句话的顺序是,I got back home. I could see the cat on the steps. This was strange.从训练的文本中取出类似这样的连续的三个句子,将中间的句子作为输入,前一个和后一个句子作为正确的输出来训练模型。模型使用的是encoder-decoder框架,并且用了RNN中的GRU来作为encoder和decoder。

Encoder的输入是第i个句子中单词的向量序列 w_{i}^{1},...,w_{i}^{N} ,GRU的重置门、遗忘门、候选隐藏层和隐藏层的公式如下:


最后时刻输出的隐藏层 h_{i} 将作为encoder的输出,也是第i个句子的单一向量表示,同时将其作为decoder的输入,decoder的重置门、遗忘门、候选隐藏层和隐藏层的公式如下:


与encoder的公式类似,不同的是多了 C_{r} , C_{z} 和 C ,分别用来分离出 h_{i} 中重置门、遗忘门、候选隐藏层的部分。每一时刻t的隐藏层 h_{i+1}^{t} 将作为decoder的输出。第i+1个句子的概率模型由以下公式给出:


其中 v_{w_{i+1}^{t}} 是单词矩阵V中 w_{i+1}^{t} 所对应的行,由这条公式即可生成第i+1个句子。生成第i-1个句子的方法同理。

目标函数是第i+1个句子和第i-1个句子概率模型的对数似然的和:


3、语义相似度(Semantic Similarity)

3.1 任务:衡量两个句子的语义相似/相关程度。

3.2 数据集:SICK Dataset (Marelli et al. 2014)

生成句子的过程:

(1)收集短的视频描述句子

(2)将句子进行主动态与被动态的转化、同义词替换等变化方式,生成新的句子

(3)将句子加入否定词或者进行反义词替换,生成反义句子

(4)打乱单词顺序生成新句子

最后人工测量这些句子的语义相关性,并用1-5进行打分。

3.3 评价过程:将两个句子输入模型计算相关度,并测量其与人工评测分数的相关性(例如Pearson相关系数)。

3.4 模型:ManhattanLSTM Model (Mueller and Thyagarajan 2016)

该模型将两个句子a和b的单词向量序列分别输入两个LSTM模型,输出分别是他们最后时刻的隐藏状态 h_{n}^{a} 和 h_{m}^{b} ,n和m分别是两个句子的长度,作差后取e^-L1作为相似度指标,如下图所示:


由于向量的L1范数是大于等于零的,所以该模型的相似度指标范围是0到1


4、文本蕴涵(Textual Entailment)

4.1 任务:文本蕴涵指的是两个文本之间有指向关系,关系分为以下三种:

Entailment:文本A为真,能推出文本B也为真

Contradiction:文本A为真,能推出文本B为假

Neutral:文本A为真不能推出文本B的真假

4.2 数据集:Stanford Natural Language Inference Dataset (Bowman et al. 2015)

生成数据步骤:

(1)取Flickr的标题作为初始数据

(2)对每个标题都分别创建它的entailed, neutral, contradicted标题

(3)人工对两两标题进行关系的评判

5、信息检索(Retrieval)

5.1 任务:给定一个输入句子,从大量数据中找出与之匹配的信息。

主要思想是,先将目标数据库中的文本编码成向量,然后将query也编码成向量,寻找数据库中与query距离最近的向量。


信息检索经常遇到的一个问题是数据库中数据量太大,导致训练太慢,所以可以只选取数据库中很小的一部分作为负样来进行负采样。

损失函数:


其中 x^{*} 是正例的输入, y^{*} 是正例的输出,S是负例样本。 s(x,y^{*}) 是负例所得的分数, s(x^{*},y^{*}) 是正例所得的分数,意思是若正例的分数比负例大于1则损失函数为0,否则会产生损失。

5.2 评价指标:

常用的评价指标有召回率和精确率。


召回率(Recall): R=\frac{TP}{TP+FN}

精确率(Precision): R=\frac{TP}{TP+FP}

由此衍生的评价指标还有:

recall@X:检索的前X个结果中是否有正确答案

MAP(mean average precision):所有queries的平均精确率

三、总结与Q&A

本文首先简要介绍了文本表示的两类方法,一类是将一个句子表示为一个向量,另一类是将一个句子表示为多个向量的序列。然后介绍了五个文本表示的应用,分别是句子分类、释义识别、语义相似度、文本蕴涵和信息检索。

下面与大家分享几个相关问答。

1、与传统句子分类方法相比,CNN的优势是什么?

答:传统的句子分类是使用SVM和Naive Bayes,它们选择的文本表示方法大多是词袋模型,会忽略词序信息。CNN用的是word-embedding来表示句子,可以保留词的序列特征。

2、本文介绍的用于Paraphrase Identification的模型skip-thought vectors的框架是什么?

答:框架是encoder-decoder模型,其中encoder和decoder都用RNN中的GRU来实现。

3、skip-thought vectors中encoder和decoder的输入输出分别是什么?

答:encoder的输入是第i个句子中单词的向量序列 w_{i}^{1},...,w_{i}^{N} ,输出是最后时刻的隐藏状态 h_{i} 。decoder的输入是encoder的输出 h_{i} ,输出是每一时刻t的隐藏状态 h_{i+1}^{t} ,用来生成句子的概率模型。

4、Manhattan LSTM Model的相似函数是什么?

答:e^-L1,即


5、本文介绍的检索评价指标有哪些?

答:召回率(Recall): R=\frac{TP}{TP+FN}

精确率(Precision): R=\frac{TP}{TP+FP}

recall@X和MAP(mean average precision)

四、参考文献

Neural Networks for NLP

papers.nips.cc/paper/5950-… (Skip-thought Vectors)

nlp.stanford.edu/sentiment/t… (Sentiment Treebank)

www.aaai.org/ocs/index.p… (Manhattan LSTM)

nlp.stanford.edu/pubs/snli_p… (Stanford Natural Language Inference Corpus)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【译】Linux概念架构 Linux内核在整个计算机系统中的

发表于 2017-11-13

Linux kernel成功的两个原因:(1)灵活的架构设计使得大量的志愿开发者能够很容易加入到开发过程中;(2)每个子系统(尤其是那些需要改进的)都具备良好的可扩展性。正是这两个原因使得Linux kernel可以不断进化和改进。

Linux内核在整个计算机系统中的位置

Fig 1 - 计算机系统分层结构

分层结构的原则:the dependencies between subsystems are from the top down: layers pictured near the top depend on lower layers, but subsystems nearer the bottom do not depend on higher layers.

这种子系统之间的依赖性只能是从上到下,也就是在上图中位于顶层的子系统依赖位于底层的子系统,反之则不行。

内核的作用

  1. 虚拟化(抽象),将计算机硬件抽象为一台虚拟机,供用户进程(process)使用;进程运行时完全不需要知道硬件是如何工作的,只要调用Linux kernel提供的虚拟接口(virtual interface)即可。
  2. 多任务处理,实际上是多个任务在并行使用计算机硬件资源,内核的任务是仲裁对资源的使用,制造每个进程都以为自己是独占系统的错觉。

PS:进程上下文切换就是要换掉程序状态字、换掉页表基地址寄存器的内容、换掉current指向的task_struct实例、换掉PC——>也就换掉了进程打开的文件(通过task_struct的files可以找到)、换掉了进程内存的执行空间(通过task_struct的mem可以找到);

Linux内核的整体架构

Linux内核的整体架构

中心系统是Process Scheduler(SCHED):所有其余的子系统都依赖于Process Scheduler,因为其余子系统都需要阻塞和恢复进程。当一个进程需要等待一个硬件动作完成时,相应子系统会阻塞这个进程;当这个硬件动作完成时,子系统会将这个进程恢复:这个阻塞和恢复动作都要依赖于Processor Scheduler完成。

上图中的每一个依赖箭头都有原因:

  • Process Scheduler依赖Memory manager:进程恢复执行时,需要依靠Memory Manager分配供它运行的内存。
  • IPC子系统依赖于Memory manager:共享内存机制是进程间通信的一种方法,运行两个进程利用同一块共享的内存空间进行信息传递。
  • VFS依赖于Network Interface:支持NFS网络文件系统;
  • VFS依赖于Memory Manager:支持ramdisk 设备
  • memory manager依赖于VFS,因为要支持swapping,可以将暂时不运行的进程换出到磁盘上的swap分区,进入挂起状态。

高度模块化设计的系统,利于分工合作。

  1. 只有极少数的程序员需要横跨多个模块开展工作,这种情况确实会发生,仅发生在当前系统需要依赖另一个子系统时;
  2. 硬件设备驱动(hardware device drivers)、文件系统模块(logical filesystem modules)、网络设备驱动(network device drivers)和网络协议模块(network protocol modules)这四个模块的可扩展性最高。

系统中的数据结构

  1. Task List
    Process Scheduler 针对每个进程维护一个数据结构task_struct;所有的进程用链表管理,形成task list;process scheduler还维护一个current指针指向当前正在占用CPU的进程。
  2. Memory Map
    Memory Manager存储每个进程的虚拟地址到物理地址的映射;并且也提供了如何换出特定的页,或者是如何进行缺页处理。这些信息存放在数据结构mm_struct中。每个进程都有一个mm_struct结构,在进程的task_struct结构中有一个指针mm指向次进程的mm_struct结构。
    在mm_struct中有一个指针pgd,指向该进程的页目录表(即存放页目录首地址)——>当该进程被调度时,此指针被换成物理地址,写入控制寄存器CR3(x86体系结构下的页基址寄存器)
  3. I-nodes
    VFS通过inodes节点表示磁盘上的文件镜像,inodes用于记录文件的物理属性。每个进程都有一个files_struct结构,用于表示该进程打开的文件,在task_struct中有个files指针。使用inodes节点可以实现文件共享。文件共享有两种方式:(1)通过同一个系统打开文件file指向同一个inodes节点,这种情况发生于父子进程间;(2)通过不同系统打开文件指向同一个inode节点,举例有硬链接;或者是两个不相关的指针打开同一个文件。
  4. Data Connection
    内核中所有的数据结构的根都在Process Scheduler维护的task list链表中。系统中每个进程的的数据结构task_struct中有一个指针mm指向它的内存映射信息;也有一个指针files指向它打开的文件(用户打开文件表);还有一个指针指向该进程打开的网络套接字。

子系统架构

Process Scheduler 架构

目标

process scheduler是Linux kernel中最重要的子系统。系统通过它来控制对CPU的访问——不仅仅是用户进程对CPU的访问,也包括其余子系统对CPU的访问。

模块

进程调度器
调度策略模块(scheduling policy module):决定哪个进程获得对CPU的访问权;调度策略应该让所有进程尽可能公平得共享CPU。

  • 体系结构相关模块(architecture-specific module)设计一组统一的抽象接口来屏蔽特定体系接口芯片的硬件细节。这个模块与CPU交互以阻塞和恢复进程。这些操作包括获取每个进程需要保存的寄存器和状态信息、执行汇编代码来完成阻塞或者恢复操作。
  • 体系结构无关模块(architecture-independent module) 与调度策略模块交互将决定下一个执行的进程,然后调用体系结构相关的代码去恢复那个进程的执行。不仅如此,这个模块还会调用memory manager的接口来确保被阻塞的进程的内存映射信息被正确得保存起来。
  • 系统调用接口模块(system call interface) 允许用户进程访问Linux Kernel明确暴露给用户进程的资源。通过一组定义合适的基本上不变的接口(POSIX标准),将用户应用程序和Linux内核解耦,使得用户进程不会受到内核变化的影响。

数据表示

调度器维护一个数据结构——task list,其中的元素时每个活动的进程task_struct实例;这个数据结构不仅仅包含用来阻塞和恢复进程的信息,也包含额外的计数和状态信息。这个数据结构在整个kernel层都可以公共访问。

依赖关系、数据流、控制流

正如前面提到过的,调度器需要调用memory manager提供的功能,去为需要恢复执行的进程选择合适的物理地址,正因为如此,所以Process Scheuler子系统依赖于内存管理子系统。当其他内核子系统需要等待硬件请求完成时,它们都依赖于进程调度子系统进行进程的阻塞和恢复。这种依赖性通过函数调用和访问共享的task list数据结构来体现。所有的内核子系统都要读或者写代表当前正在运行进程的数据结构,因此形成了贯穿整个系统的双向数据流。

除了内核层的数据流和控制流,OS服务层还给用户进程提供注册定时器的接口。这形成了由调度器对用户进程的控制流。通常唤醒睡眠进程的用例不在正常的控制流范围,因为用户进程无法预知何时被唤醒。最后,调度器与CPU交互来阻塞和恢复进程,这又形成它们之间的数据流和控制流——CPU负责打断当前正在运行的进程,并允许内核调度其他的进程运行。

Memory Manager 架构

目标

内存管理模块负责控制进程如何访问物理内存资源。通过硬件内存管理系统(MMU)管理进程虚拟内存和机器物理内存之间的映射。每一个进程都有自己独立的虚拟内存空间,所以两个进程可能有相同的虚拟地址,但是它们实际上在不同的物理内存区域运行。MMU提供内存保护,让两个进程的物理内存空间不互相干扰。内存管理模块还支持swap——将暂时不用的内存页换出到磁盘上的swap分区,这种技术让进程的虚拟地址空间大于物理内存的大小。虚拟地址空间的大小由机器字长决定。

模块

内存管理子系统

  • 架构相关模块(architecture specific module)提供访问物理内存的虚拟接口;
  • 架构无关模块(architecture independent module)负责每个进程的地址映射以及虚拟内存交换。当发生缺页错误时,由该模块负责决定哪个内存页应该被换出内存——因为这个内存页换出选择算法几乎不需要改动,所以这里没有建立一个独立的策略模块。
  • 系统调用接口(system call interface) 为用户进程提供严格的访问接口(malloc和free;mmap和ummap)。这个模块允许用进程分配和释放内存、执行内存映射文件操作。

数据表示

内存管理存放每个进程的虚拟内存到物理内存的映射信息。这种映射信息存放在mm_struct结构实例中,这个实例的指针又存放在每个进程的task_struct中。除了存放映射信息,数据块中还应该存放关于内存管理器如何获取和存储页的信息。例如:可执行代码能够将可执行镜像作为备份存储;但是动态申请的数据则必须备份到系统页中。(这个没看懂,请高手解惑?)
最后,内存管理模块还应该存放访问和技术信息,以保证系统的安全。

依赖关系、数据流和控制流

内存管理器控制物理内存,当page fault发生时,接受硬件的通知(缺页中断)—— 这意味着在内存管理模块和内存管理硬件之间存在双向的数据流和控制流。内存管理也依赖文件系统来支持swapping和内存映射I/O——这种需求意味着内存管理器需要调用对文件系统提供的函数接口(procedure calls),往磁盘中存放内存页和从磁盘中取内存页。因为文件系统请求非常慢,所以在等待内存页被换入之前,内存管理器要让进程需要进入休眠——这种需求让内存管理器调用process scheduler的接口。由于每个进程的内存映射存放在进程调度器的数据结构中,所以在内存管理器和进程调度器之间也有双向的数据流和控制流。用户进程可以建立新的进程地址空间,并且能够感知缺页错误——这里需要来自内存管理器的控制流。一般来说没有用户进程到内存管理器的数据流,但是用户进程却可以通过select系统调用,从内存管理器获取一些信息。

Virtual File System 架构

目标

虚拟文件系统为存储在硬件设备上数据提供统一的访问接口。可以兼容不同的文件系统(ext2,ext4,ntf等等)。计算机中几乎所有的硬件设备都被表示为一个通用的设备驱动接口。逻辑文件系统促进与其他操作系统标准的兼容性,并且允许开发者以不同的策略实现文件系统。虚拟文件系统更进一步,允许系统管理员在任何设备上挂载任何逻辑文件系统。虚拟文件系统封装物理设备和逻辑文件系统的细节,并且允许用户进程使用统一的接口访问文件。

除了传统的文件系统目标,VFS也负责装载新的可执行文件。这个任务由逻辑文件系统模块完成,使得Linux可以支持多种可执行文件。

模块

虚拟文件系统模块

  • 设备驱动模块(device driver module)
  • 设备独立接口模块(Device Independent Interface):提供所有设备的同一视图
  • 逻辑文件系统(logical file system):针对每种支持的文件系统
  • 系统独立接口(system independent interface)提供硬件资源和逻辑文件系统都无关的接口,这个模块通过块设备节点或者字符设备节点提供所有的资源。
  • 系统调用模块(system call interface)提供用户进程对文件系统的统一控制访问。虚拟文件系统为用户进程屏蔽了所有特殊的特性。

数据表示

所有文件使用i-nodes表示。每个inode都记录一个文件在硬件设备上的位置信息。不仅如此,inode还存放着指向逻辑文件系统模块和设备驱动的的函数指针,这些指针能够执行具体的读写操作。通过按照这种形式(就是面向对象中的虚函数的思想)存放函数指针,具体的逻辑文件系统和设备驱动可以向内核注册自己而不需要内核依赖具体的模块特性。

依赖关系、数据流和控制流

一个特殊的设备驱动是ramdisk,这个设备在主存中开辟一片区域,并把它当成持久性存储设备使用。这个设备驱动使用内存管理模块完成任务,所以在VFS与对内存管理模块存在依赖关系(图中的依赖关系反了,应该是VFS依赖于内存管理模块)、数据流和控制流。

逻辑文件系统支持网络文件系统。这个文件系统像访问本地文件一样,从另一台机器上访问文件。为了实现这个功能,一种逻辑文件系统通过网络子系统完成它的任务——这引入了VFS对网络子系统的一个依赖关系以及它们之间的控制流和数据流。

正如前面提到的,内存管理器使用VFS完成内存swap功能和内存映射I/O。另外,当VFS等待硬件请求完成时,VFS需要使用进程调度器阻塞进程;当请求完成时,VFS需要通过进程调度器唤醒进程。最后,系统调用接口允许用户进程调用来存取数据。不像前面的子系统,VFS没有提供给用户注册不明确调用的机制,所以没有从VFS到用户进程的控制流。

Network Interface 架构

目标

网络子系统让Linux系统能够通过网络与其他系统相连。这个子系统支持很多硬件设备,也支持很多网络协议。网络子系统将硬件和协议的实现细节都屏蔽掉,并抽象出简单易用的接口供用户进程和其他子系统使用——用户进程和其余子系统不需要知道硬件设备和协议的细节。

模块

网络协议层模块图

  • 网络设备驱动模块(network device drivers)
  • 设备独立接口模块(device independent interface module)提供所有硬件设备的一致访问接口,使得高层子系统不需要知道硬件的细节信息。
  • 网络协议模块(network protocol modules)负责实现每一个网络传输协议,例如:TCP,UDP,IP,HTTP,ARP等等~
  • 协议无关模块(protocol independent interface)提供独立于具体协议和具体硬件设备的一致性接口。这使得其余内核子系统无需依赖特定的协议或者设备就能访问网络。
  • 系统调用接口模块(system calls interface)规定了用户进程可以访问的网络编程API

数据表示

每个网络对象都被表示为一个套接字(socket)。套接字与进程关联的方法和i-nodes节点相同。通过两个task_struct指向同一个套接字,套接字可以被多个进程共享。

数据流,控制流和依赖关系

当网络子系统需要等待硬件请求完成时,它需要通过进程调度系统将进程阻塞和唤醒——这形成了网络子系统和进程调度子系统之间的控制流和数据流。不仅如此,虚拟文件系统通过网络子系统实现网络文件系统(NFS)——这形成了VFS和网络子系统指甲的数据流和控制流。

结论

1、Linux内核是整个Linux系统中的一层。内核从概念上由五个主要的子系统构成:进程调度器模块、内存管理模块、虚拟文件系统、网络接口模块和进程间通信模块。这些模块之间通过函数调用和共享数据结构进行数据交互。、

2、Linux内核架构促进了他的成功,这种架构使得大量的志愿开发人员可以合适得分工合作,并且使得各个特定的模块便于扩展。

  • 可扩展性一:Linux架构通过一项数据抽象技术使得这些子系统成为可扩展的——每个具体的硬件设备驱动都实现为单独的模块,该模块支持内核提供的统一的接口。通过这种方式,个人开发者只需要和其他内核开发者做最少的交互,就可以为Linux内核添加新的设备驱动。
  • 可扩展性二:Linux内核支持多种不同的体系结构。在每个子系统中,都将体系结构相关的代码分割出来,形成单独的模块。通过这种方法,一些厂家在推出他们自己的芯片时,他们的内核开发小组只需要重新实现内核中机器相关的代码,就可以讲内核移植到新的芯片上运行。

参考文章:

  1. www.cs.cmu.edu/afs/cs/proj…
  2. www.fceia.unr.edu.ar/ingsoft/mon…
  3. 内核源码:lxr.oss.org.cn/

声明:本文翻译自Conceptual Architecture of the Linux Kernel

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Docker周报第149期

发表于 2017-11-13

学习教程

瓜子云的任务调度系统

瓜子云的任务调度系统结合了Kubernetes的Job和Airflow。并在Airflow基础上加了很多改动。本文跟大家交流瓜子云的调度系统的架构设计和解决方案。

DockOne.io
因Kubernetes漏洞导致银行应用瘫痪1小时,事件细节回顾和分析


手机银行Monzo经历了自成立来历史上最大的一次技术事故,技术主管Oliver在论坛上兑现承诺公布了整个事件的细节,并对未来技术要求作出了新的承诺。下面是Monzo技术主管Oliver在论坛再现最黑暗的一个半小时的记录。

Monzo技术博客
从架构演进的角度聊聊Spring Cloud都做了些什么?


Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cloud在微服务的架构中都做了哪些事情?Spring Cloud提供的这些功能对微服务的架构提供了怎样的便利?

个人博客
容器和分布式系统:从哪里来,将会去哪里


Mesosphere首席执行官(CEO)Florian Leibert最近和Chuck McManis进行了一次座谈,内容关于早期研发分布式系统遇到的挑战、容器技术的起源和计算的未来。如果你有看Hacker News,很有可能你已经认识他了,笔名为ChuckMcM的他很活跃。这次惊世的对话涉及到Chuck和他团队早期在建立分布式系统时所解决的问题,学到的经验教训和如何形成现在的架构–同时很有可能是计算的未来。

Mesosphere
携程容器云优化实践


随着微服务架构的流行,把容器技术推到了一个至高点上;而随着Docker,K8S等容器技术的日趋成熟,DevOps的概念也再次热度上升;面对容器化的大潮趋势,各家公司都在积极地响应和实践,携程也在这方面做了不少工作,形成了自己的容器云平台。

携程技术中心
项目实施DevOps时,我们是如何做测试的


正如我们所知,DevOps最近几年很风靡,很多企业正在如火如荼的推行它。然而,你可曾想过,从传统到敏捷、再到DevOps,开发模式的不断革新对测试提出了怎样的挑战?

Thoughtworks
OPS基于Mesos/Docker构建的Elasticsearch容器化私有云


本文主要介绍 OPS 团队利用 Mesos 资源管理平台和 Docker 容器技术所构建的 Elasticsearch 容器化的私有云。

Qunar技术沙龙
Kubernetes ConfigMap热更新测试


ConfigMap是用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中,本文主要是探究 ConfigMap 的创建和更新流程,以及对 ConfigMap 更新后容器内挂载的内容是否同步更新的测试。

个人博客
Kubernetes 1.8抢占式调度Preemption源码分析


本博文是对Kubernetes 1.8中基于Pod优先级抢占式调度Preemption的源码分析,整个过程主要围绕这scheduler的相关源码进行分析,从源码角度解读抢占式调度的全过程,相信从中你会学到很多在官方文档中你看不到但又很重要的细节,比如抢占的最佳节点到底是怎么选出来的?竟然还跟scheduler extender相关?

个人博客

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

有赞11·11:全链路压测方案设计与实施详解

发表于 2017-11-13

2017年双十一即将来临,对于买家来说是一年一度的购物狂欢,可是对于电商公司的技术人员来说,却是一年一次的大考。如何用更少的预算完成指定当前业务规模的流量高峰,是技术的永恒主题。

由InfoQ举办的ArchSummit全球架构师峰会即将于12月8-11日北京举行,大会与阿里巴巴合作策划了双11架构专场,并邀请了顶级技术专家担任出品人,设置了“新一代DevOps”、“人工智能与业务应用”、“架构升级与优化”等17个热门话题,目前
大会日程已出,欢迎前来讨论交流。

有赞在双十一之前完成了全链路压测方案,并把它用于大促的扩容和容量验证,取得了不错的成果。

在电商公司待过的技术同学都知道,在大促来临时,整个集群的最高峰压力将是正常时间的几十倍,最高峰持续的时间会特别短,然后回落到正常水平的几倍。所以,我们可能会自然而然地想到,把整个集群扩容几十倍的机器,在双十一当天应对几十倍的流量,然后第二天减至正常量,就可以完成大促的考验。事实情况是否真的这么简单?

大促保障的困难

用户购买商品的链路是一条很长很复杂的系统集群,中间会涉及到店铺、商品、会员、营销、交易、支付等6大核心模块,每个模块又会涉及到多个不同的服务化系统单元,我们把这一条骨干的链路就叫做核心链路。

大家都知道,双十一当天,真正爆增的其实是买家的购买量,像开店/商品上架等功能,其实并发量没什么变化。也就是说,真正的压力其实是在核心链路上面,如果把所有的系统都扩容几十倍,本身就是一个很大的浪费。正常来说,一个稍有规模的电商公司,日常有几千台机器维持正常的运转,本身就是一个较大的开销,如果突增几十倍的系统开销,对于公司的财务也是很大的压力。所以,一个较理想的方法,是只把核心链路的系统扩大几十倍的系统吞吐量,就可以达到目标。

困难一

大型的分布式系统其实错综复杂,公司需要维持成百上千的服务化系统。理论上来说, 只有少部分系统是核心链路的系统。但是在实际情况下,因为公司人员的关系,可能会把某些非核心系统,不知不觉加入到了核心链路中。所以,第一件要做的事情,就是把非核心系统从核心链路上剔除。

困难二

一般公司都会在线下搭建性能测试环境,在该环境下,我们的测试同学可 以借助一些测试工具,去压单机单接口的性能。假如,店铺的首页面,我们在性能测试环境下,得出单机单接口的QPS峰值是500,这是否意味着, 要达到10w的QPS,我只需要设置200台机器就可以了呢?答案当然是否定的。因为任何的页面接口都不是单独存在的,都会受到公共资源的制约,如:DB、Redis、MQ、网络带宽等。比如当店铺首页达到10w QPS的时候,商品详情页可能要达到8w的QPS了,也会消耗公共资源。当公共资源的能力下降时,势必会影响所有的系统。所以,通过单机性能算出来的理论值,跟实际情况会相差很远。

困难三

正常来说,任何大促都会有业务目标,这个目标一般是拿GMV进行评估。但是我们在进行系统容量评估的时候,一般会想扩大多少台机器。那么GMV跟核心链路各个系统之间的机器数量的转化关系是什么样的?

困难四

做过大型分布式系统的同学,可能都知道一个事实,即整个集群的性能其实取决于接口的短板效应。而这个短板的接口,在正常的流量下,是不会显现出来的。只有集群的整体压力达到一定值的情况下,才会偶尔显现, 然后造成雪崩效应,拖累整个系统集群。所以,如何在大促之前找到这些短板,然后把它们一个一个优化,这件事情就显得非常重要。

困难五

应用系统的扩容相对而言是比较简单的,完成大促之后,可以很容易归还。但是DB等核心资源的扩容其实并不容易,而且资源不可能归还(数据不不可丢失)。

事实是检验真理理的唯⼀一标准,上面提到的五个困难,其实都可以用线上真实压测的办法去检验。业内大型电商公司,会用全链路压测的方案去指导扩容的进程,有赞也不例外。今年双十一,有赞用该方案完成了对核心链路20倍的扩容,但是整个集群的规模只是扩大了了一倍多一点。

有赞全链路压测的设计方案

全链路压测的目标是让双十一要发生的事情提前发生,并验证在该情况下系统表现良好。做线上压测,有一个很重要的原则:线上系统是不允许有脏数据的。

有赞的压测设计方案,可以用几句简单的话做概括:

  • 压测流量落影子库,正常流量落正常库。
  • 压测不能把系统压崩溃。
  • 压测是模拟双十一流量最高峰时用户的购物行为,所以综合性的流量模型需要跟实际情况相符。

全链路压测的总体设计

有图有真相,我们先上图。

在上述图中,我们明显可以看到,全链路压测有几个关键部分:

  1. 数据工厂,负责造请求数据。
  2. 大流量下发器,产生很大的压力去压系统。
  3. 线上服务集群同时处理压测请求+正常请求。
  4. 压测流量落影子存储,正常流量落正常存储。
  5. 压测流量对于外部的依赖走mock服务器,正常流量走正常外部集群。
  6. 水位检测,需要检测存储+线上应用服务器的健康度,并且能够干预流量下发。

关键模块的设计方案

数据工厂设计

数据工厂是压测的一个核心部件,主要由Hive表的集合+各种导入、导出脚本组成。

数据工厂的目的是保存压测需要准备的所有数据,数据需要做清洗,比如:

  • 商品未下架
  • 商品的库存无限
  • 营销活动的信息有效,未过期
  • 店铺未关闭等等

场景的定义:场景的定义关系到数据的准备,正常来说,压测只会压随着买家人数暴增、系统的压力立即增加的场景,我们把这个场景涉及到的系统叫做“核心链路”。

影子存储的设计与路由能力

  1. DB、路由方式由RDS提供,存储可以有两种方式:
    • 影子表与正常表存在同样的instance、不同的schema中。这个方案不需要增加额外的存储开销,相对更便宜,但是风险较高(把库压死了会影响线上业务)。
    • 影子表与正常表存在不同的instance、相同的schema中。这个方案相对较贵,需要额外搭建DB集群,但是安全性较高。我们选择的是第一个方案。
  2. Redis:通过key值来区分。压测流量的key值加统一前缀,通过Redis-Client做路由。
  3. HBase:通过命名空间做隔离。影子空间加前缀,提供统一的HBase Client做数据访问,由该Client做路由。
  4. ES:通过index名字来区分。影子的索引统一加前缀,提供统一的ES Client做数据访问,由该Client做路由。

线上应用集群的变更

  1. 统一线上应用对于数据的访问(DB+ES+HBase+Redis),提供统一的Client。
  2. 由于线上的应用都是服务化工程,远程调用时,必须具备压测流量的标记透传能力。
  3. 线上的少部分应用,需要访问第三方服务,比如:物流、支付。这些应用需要改造为压测的流量直接访问mock服务器。

全布式流量下发器设计与链路设计的能力

我们选用gatling作为我们的流量下发器实现。

数据文件的内容

每一种场景都有不同的数据文件,数据文件由场景相对应的多种url组合而成。比如:我们本次压测会压“无优惠的场景、秒杀场景、满减场景、拼团场景” 等等。无优惠的场景分为“店铺首页、商品详情页、加购物车页、下单页、支付页、支付成功页”等等。这个文件不涉及漏斗转化率。一般来说,一个数据文件很大(至少是G级别的)。所以我们的数据文件内容格式为:

  • 所有的数据⽂文件
    • 无优惠的场景数据文件
      • 场景集合1
        • 店铺首页URL
        • 商品详情页URL
        • 加购物车页URL
        • 下单页URL
        • 支付页URL
        • 支付成功页URL
      • 场景集合2
        • 店铺首页URL
        • 商品详情页URL
        • 加购物车页URL
        • 下单页URL
        • 支付页URL
        • 支付成功页URL
    • 秒杀场景数据文件
    • 满减场景数据文件
    • 拼团场景

流量下发脚本的内容

流量下发脚本的核心是控制漏斗转化率:

  1. 不同场景的流量配比。
  2. 每个场景下面,url从上往下的漏斗转化率。

gatling提供天然的转化率配置脚本,用起来非常方便。有兴趣的同学可以自行Google。

水位检测系统的能力设计

这个是一个很重要的模块,在项目启动之初,我们希望以实时计算的方式,一边采集各个应用系统的资源使用情况+接口耗时+业务正确率,一边向gatling发送流量干预信号,以做到自动保护系统的目的。由于时间关系,我们并未实现这一方案。取而代之的是人肉查看实时监控界面的方式,人为去干预gatling的流量下发情况。

全链路路压测的实施

如果实施过全链路压测的项目,大家都会有一个共同的感受:做基础的组件容易,让核心业务去完成相关的升级与验证工作很难。原因只有一个:需要用全链路压测的公司,业务规模都很大,涉及的团队会特别多。梳理理清楚庞大的业务,让所有的业务团队一起发力,本身就是一件很难的事情。

我们把链路压测的实施分为以下几个阶段:

  1. 基础中间件开发,各种框架升级开发,压测器研究与脚本开发。
  2. 业务升级与线下验证(人工点击,数据落影子库)
  3. 业务升级与线上验证(人工点击,数据落影子库)
  4. 数据工厂数据准备。
  5. 小流量下发验证(用gatling下发,数据落影子库)
  6. 大流量量压测与系统扩容

第2、3、5阶段,需要借助业务测试同学的力量;第4阶段,需要借助业务开发同学的力量;第6阶段,则需要借助业务团队+运维同学的力量。

由于每个阶段人员都不太一样,所以需要每一个阶段都组织不同成员的虚 拟小组,借助各个团队的力量完成相应的工作。

压测过程中的重要细节与把控

流量爬升的规律

正常来说,在大促之前做压测,目的一般是给扩容/优化做方向性的指导。

假设我们双十一需要扩大20倍的容量以应对高峰,那我一定不会一开始 就拿20倍的流量去压我们的系统,因为这样做的话,所有的系统都会在一瞬间就挂掉,这样没有任何意义。我们的做法是,阶段性的爬坡打流量,然后把系统的能力一段一段提升上去。

例如:

  1. 第一天,我们会以日常流量的最高峰为起始流量,然后爬坡到一个流量高峰A,记为第一天的目标。在压测之前先做一次扩容。在压测中,碰到了某个瓶颈了,通过增加该系统的机器来提升能力。
  2. 第二天,我们以A为起始流量,然后再次爬坡到B。同样压测前做扩容+压测中碰到瓶颈加机器。
  3. 以此类推,一直到最终流量达到目标流量为止。
  4. 每一天的压测,也需要以慢慢爬坡的方式提升流量。在爬坡的某个高度稳定5分钟,然后再次爬坡。稳定时间5分钟,爬坡时间30秒。

非核心链路进核心链路的问题

发现并解决这个问题,本身就是压测的目的之一。

正常来说,非核心链路,在大促来临时不会扩大多少容量。当压测的压力增大时,很容易通过系统报警查到。

当发现这个问题的时候,一定要坚决要求业务方做系统改造,把非核心系 统的强依赖去掉。解耦的技术有很多,需要根据不同的业务规则来选择方案。比如:业务降级、通过中间件解耦、异步化等。

上下游扩容/代码优化的选择

一般来说,在压测的过程中,当碰到压测流量不能再升高的时候,会有很多原因,我们碰到的情况有以下几种:

  1. 下游的某些服务化工程的能力达到瓶颈了,导致网关RT值升高,拖累整个集群的QPS上不去。
  2. 网关应用自身的能力达到瓶颈。
  3. 中间件/DB能力达到瓶颈。
  4. job的能力达到瓶颈,导致数据处理不够及时。
  5. 流量集中的页面,消耗了集群大量资源,如:店铺首页、商品详情页等。

针对2、3、4这样的情况,我们的选择是毫不犹豫地加机器,代码优化的性价比较低。

针对第1种情况,需要做一些分析,如果这样的能力是在系统设计者的预期之内的,可以选择加机器,如果完全超乎意料的,一定需要通过程序优化来提升能力,否则加了资源,可能还是瓶颈。

针对第5种情况,一定要做的事情是静态化。因为这些流量集中页面,一般都是展示性质的。不管如何做应用内的优化,获得的能力提升远不如做静态化的收益大。

未来展望

全链路压测的方案有赞只是初试牛刀,我们已经看到了这个方案在提升+验证集群处理能力方面巨大的价值。当前,这个方案做得还较粗糙,存在一些问题:

  1. 压测只能在夜间做。
  2. 压测中需要有很多业务开发人员陪同。
  3. 链路规划复杂度太高。
  4. 压测控制台的稳定性还不够高。
  5. 水位检测与流量干预是通过肉眼观察监控来实现。

后续我们团队会继续投入大量精力去完善整个方案。希望可以将压测方案变成:

  1. 线上测试链路的机器人,实时检测线上系统的正确性,同时没有脏数据干扰。
  2. 测试同学手里的工具,做到流量压测常规化,开发同学不用陪同。
  3. 压测可以在白天进行,晚上熬夜毕竟不利于健康。
  4. 链路规划图形化,并与数据工厂结合,完成数据的准备工作。
  5. 通过水位检测与流量干预来保护系统,让业务系统不会被压崩溃。

作者介绍

金瑞敏,有赞核心交易、java框架团队负责人。带领团队完成核心交易平台化体系建设,优化有赞服务化治理方案、环境隔离方案、全链路压测方案等等。长期从事分布式系统的建设与研究。


相关内容

当当11·11:高可用移动入口与搜索新架构实践

国美11·11:大促场景下的国美智能推荐系统演进之路

苏宁11·11:Monitor在左,Control在右,监与控的结合——深度剖析穆加决策分析平台

苏宁11·11:从0到1,苏宁API网关的演进之路

蘑菇街11·11:图像算法在电商大促中的应用浅析

相关厂商内容

这可能是最好的技术内容搜索引擎

京东虚拟商品系统如何做高可用架构设计?

顶尖架构师岁末总结大会:上百技术案例日程出炉!

不改一行代码:左耳朵看微服务调度的三大件

AI时代,我到底该如何跟上技术潮流?

相关赞助商

您好,朋友!

您需要 注册一个InfoQ账号 或者 登录 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

社区评论
Watch Thread
关闭
关闭
关闭

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

腾讯云11·11:千亿订单背后的安全“暗战”

发表于 2017-11-13

每年的电商大促,就像是一次次的系统检阅仪式,接受着来自用户、同行以及老板的审视。而在一次次订单量记录刷新,成交额飙出新高的同时,平台架构也在面临巨大的挑战,如页面打不开、服务不可用、优惠券被薅、网络被攻击、支付延迟等都有可能发生。那么针对这些问题,腾讯云是如何助力其电商客户解决?本文将从海量并发、安全性、用户体验几个方面,深入讲解腾讯云电商平台最佳实践方案。

另外,由InfoQ举办的ArchSummit全球架构师峰会即将于12月8-11日北京举行,大会与阿里巴巴合作策划了双11架构专场,并邀请了顶级技术专家担任出品人,设置了“新一代DevOps”、“人工智能与业务应用”、“架构升级与优化”等17个热门话题,目前
大会日程已出,欢迎前来讨论交流。

海量并发弹性扩容

回顾近年来的电商大促,最具特色的便是抢购、秒杀活动了,而这也使得Web 访问量可能瞬间陡增十倍甚至是数十倍,对接入层、逻辑层的按需、实时、快速平行扩展能力提出了较高的要求,如选用传统的硬件设备搭建集群,会遇到成本高昂,运维繁琐等问题。

对于这些问题,可以采用负载均衡配合云监控、弹性伸缩(Auto Scaling)、消息队列、分布式缓存、分布式数据库等服务来解决。

  • 负载均衡服务:CLB 单集群的最大并发连接数超过1.2亿,可处理峰值40Gbps的流量,每秒处理包量为600万,负载均衡服务曾在历年的春节微信“抢红包”历练;
  • 弹性伸缩可以根据需求和策略,比如CPU利用率达到阀值,就自动扩容指定数量的云服务器,也可根据定时、周期或监控策略,相应地增加或减少CVM实例,并完成配置,保证业务平稳健康运行。

对于自建IDC的客户,则可以采用混合云的模式,以专线将腾讯云与其自建IDC连接起来,这样仅在公有云上部署其接入、逻辑、cache层,就可充分复用公有云的BGP接入及弹性的能力。

腾讯云安全体系防御原理及过程分析

安全性对于电商平台来说,不仅仅在大促期间会一而再再而三地关注到,它更是一个日常话题,网站入口、支付等环节,都往往处于“高危”环境,来自于网络“黑客”及“黑产”团队的恶意攻击等,都有可能会导致网站核心业务不可用、运营投入的大量优惠券被“羊毛党”抢占,甚至给后台系统带来“致命”打击。

腾讯云今年推出了AI安全战略,以大数据和AI的算法为驱动,构建应用于安全领域的包括社交图谱分析、图像自动识别、自然语言处理、知识表达推理等AI通用能力,形成智能身份鉴定、威胁情报分析、异常流量检测、网络攻击溯源、人机行为识别、恶意图片识别、垃圾文本检测等7项技术应用。

这些能力通过多款产品,如业务安全(天御)、主机安全(云镜)、数据安全(数盾)、移动安全(乐固)、账号安全(祝融)、网站与流量安全、内容安全与风控安全,为客户提供安全保障。本次我们将重点放在电商客户最常遇到的网络安全和业务安全展开介绍。

网络安全检测系统及防御原理

BGP高防(大禹)产品是腾讯云针对电商网站遭受大流量 DDoS 攻击时服务不可用的情况推出的增值服务,提供300G 的防护服务并拥有35线的 BGP 线路,帮助客户全面应对DDoS 攻击的挑战,同时改善客户的访问体验。


图:腾讯云DDos防护体系架构

BGP高防攻击检测

对于BGP的高防攻击检测,腾讯云采用光棱镜物理分光来做1:1流量镜像,旁路Netflow检测镜像流量,不影响客户正常业务流向,检测后的镜像流量被丢弃。检测原理主要是基于流量建模分析客户IP的流量中是否存在攻击流量。

  • 对于清洗攻击,在检测到某个IP被攻击后,清洗集群向核心路由发布BGP牵引路由,将该IP的流量牵引至清洗集群防护。清洗后的干净流量,再通过BGP路由回注至核心路由,最终流至客户的云主机或通过转发集群流至客户在腾讯云外的机房。
  • 防护算法主要是基于IP/TCP/UDP协议的缺陷检测及HTTP、HTTPS报头的分析,不涉及、不查阅业务负载报文,防护系统对客户的业务数据完全无感知。
  • 云内客户通过高防包绑定需要防护的设备,来提升防护级别。
    • BGP高防专区除了提供高防服务,还同时对腾讯云客户的公网IP提供基础防护
    • 业务部署在腾讯云的客户,可以将绑定高防包绑定至需要防护的设备,提升防护级别。

  • 云外客户通过高防IP牵引攻击流量,保护后端业务
    • 客户在腾讯云高防IP上配置业务转发规则;
    • 客户将高防IP作为业务IP对外发布;
    • 所有流量都先经过腾讯高防IP,再转发到客户真实源站,攻击流量在高防机房被清洗。
    • 高防IP基于公网IP回源,在其他云或IDC机房的业务,都可以接入腾讯云高防IP的防护

网络安全防御实践

大禹是腾讯云AI安全战略的网站安全防御系统,大禹网站高防可抵御SYN Flood、ACK Flood、UDP Flood、NTP Flood、SSDP Flood、DNS Flood、HTTP Flood、ICMP Flood、CC等各类攻击。防护与腾讯安全大数据平台联动,实时更新防护策略库,有效防护新攻击手法。

  • DDos防护:大禹 BGP 高防基于先进特征识别算法进行精确清洗,抵御 Syn Flood、ICMP Flood 等各种大流量攻击。业务接入大禹 BGP 高防后,该功能自动开启,无论是 SYN Flood、UDP Flood 还是其他类型的大流量攻击,大禹 BGP 高防系统单节点能够防御300Gbps攻击。
  • CC防护:大禹 BGP 高防通过模式识别、身份识别等多种手段,来识别恶意访问者,同时采用重认证、验证码、访问控制等手段,抵御 http get 等各类应用层攻击。恶意竞争者和黑产从业者将无法通过应用层攻击威胁到客户的业务服务器。

腾讯目前通过自建骨干网的CAP平台,BGP链路对接了35家运营商,其中包括中国电信、中国联通、中国移动、中国教育和科研计算机网、中国科技网5家运营商。其余为中小运营商,包括:鹏博士、长宽以及各地广电等宽带接入服务商。

腾讯云业务安全防御框架深度解析

“羊毛党”双十一刷单流程

“羊毛党”其实早已经从个体行为、小作坊发展成了产业链,他们往往有着明确的分工,已形成几大团伙。下图为电商刷单团队的工作模式和任务分工。


图:电商刷单团队的工作流程

具体操作方式:

1.软件制作团伙:专门制作各种自动、半自动的黑产工具,比如注册自动机、刷单自动机等;他们主要靠出售各种黑产工具、提供升级服务等形式来获利。

2.短信代接平台:实现手机短信的自动收发。这其中,有一些短信平台是亦正亦邪,不但提供给正常的商家使用,一些黑产也会购买相关的服务。

3.账号出售团伙:他们主要是大量注册各种账号,通过转卖账号来获利;该团伙与刷单团伙往往属于同一团伙。

4.刷单团伙:到各种电商平台刷单,获取优惠,并且通过第三方的电商平台出售优惠,实现套现。

腾讯云天御系统安全监测方案

腾讯天御产品通过腾讯积累的安全大数据和防刷引擎,精准识别“薅羊毛”的恶意行为,避免企业被刷带来的巨大经济损失。其防御过程大概如下:

1.通过天御防刷,判定请求流量是否为恶意(API实时接口);

2.之后,天御结果200毫秒内返回,厂商可根据返回的风险值(level),共计5个档位做合适的处理:

3.若厂商需要做精细化运营,或者希望对某种异常标签做针对性的打击。可参考risktype返回的标签。通常根据风险值(level)做处理已足够。在风险值level不为0的情况下,risktype可任意组合(根据命中异常的标签实际情况)。

风险防御处理案例解析

以下为某厂商根据天御返回的风险值(level)进行处理的实际案例:

基于输入参数的实时分析系统,确保每次请求都实时评估判定:

  • 传统黑名单机制,极易造成用户投诉。比如手机重放号、账号被盗后找回等,因历史作恶判定为黑而永久黑;
  • 天御的实时判定服务,确保在不同的环境(如IP)下,实时关联数据亦不相同。所以每次请求,即使是同一个账号,亦会做出公允的评判
  • 天御实时模型,在大量业务中学习和训练。这是整个服务的核心引擎,确保高可用高覆盖

创新玩法,提升用户体验

除了基础稳定性、安全性的保障,越来越多的电商平台,也在寻找新的模式、新的玩法,来提升自身平台的用户体验,提升转化率。腾讯云就现金电商平台的业务需求,总结了以下三大创新应用:

  • 智能推荐,是最普适的一个诉求,面对不同的用户,根据客户的特征、喜好,展现不同的商品,一来可以提升用户好感度,另一方面也是提升转化率的良方。但这对于一般企业来说,是个漫长的周期,需要投入大量的人力和时间,不断地积累数据,不断地打磨算法,才能有所见效。腾讯云依托腾讯在电商、游戏、金融、泛娱乐、资讯及 3C 等多领域深厚的大数据技术积累,为客户提供基于海量用户画像 + 实时大数据机器学习的内容个性化推荐 PaaS 服务。
  • 电商+直播。说到新玩法,“电商+直播”绝对是一个绕不开的火热话题,然而自主研发,却是难熬的等待,腾讯云基于腾讯多年在视频领域的经验,推出一站式解决方案,从全平台的推流主播SDK,到海量云端处理系统,再到强大的CDN云端加速,最终到用户播放SDK,无缝链接,24小时即可完成接入。
  • 当然,如今备受关注的小程序,也是一个不得不提的话题,据统计,蘑菇街女装精选小程序7月4日正式上线,截至目前已经获取了6000万的新客户。到9月份,日均访问量增长较7月超过200%,GMV增长超过140%。而小程序现有的SDK/DEMO缺乏对云端的支持,依赖开发者逐个模块搭建云端服务,过程繁琐。腾讯云提供了一键构建具备云端能力的专属小程序,提高小程序开发的效率。此外,还提供PaaS级的WebSocket信道服务,降低了开发者使用WebSocket通信的门槛。同时,通过提供完整的鉴权会话管理服务,来保证用户的信息安全。

写在最后

本文通过云端海量并发弹性扩容、AI安全体系防御构建与实施、电商领域的创新应用三大板块介绍了腾讯云如何在双十一电商大促的情境下,为电商平台提供可用、高效、完善的安全护航方案。电商与黑产之间的较量从来不会结束,双十一来临之际,我们希望通过一些对抗黑产的新经验和技术的分享,给电商从业人员以及相关的开发者提供一些新的思路和借鉴。

作者介绍

戴唯伟,腾讯云解决方案资深架构师,5年传统SI技术工作经历,5年公有云上云架构咨询经历。上云架构“老司机”,曾就职于微软云、金山云,对国内外云厂商产品发展、各类用户上云历程有很多“切身体会”。深谙传统企业客户、游戏行业客户、互联网行业客户“上云”和“用云”的痛点、难点和机遇,职业理想是做云计算和AI殿堂里的“扫地僧”。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SpringMvc4x高级配置(一) 文件上传配置

发表于 2017-11-13

一. 点睛

文件上传是一个项目里经常要用到的功能,Spring MVC通过配置一个MultipartResolver来上传文件。

在Spring的控制器中,通过MultipartFile file来接收文件,通过MultipartFile[] files接收多个文件上传。

二. 示例

  1. 添加文件上传依赖

1
2
3
4
5
6
7
8
9
10
11
12
复制代码<!-- file upload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 非必需,可简化IO操作 -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
  1. 增加上传页面upload.jsp。

在src/main/resources/views下新建upload.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
复制代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>upload page</title>

</head>
<body>

<div class="upload">
<form action="upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/><br/>
<input type="submit" value="上传">
</form>
</div>


</body>
</html>
  1. 添加转向到upload页面的ViewController

在文件MyMvcConfig的addViewControllers方法里面增加下面的转向配置,代码如下:

1
复制代码registry.addViewController("/toUpload").setViewName("/upload");

添加完成之后的代码如下所示:

1
2
3
4
5
6
复制代码
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("/index");
registry.addViewController("/toUpload").setViewName("/upload");
}
  1. MultipartResolver配置

在文件MyMvcConfig中增加下面的代码,提供对上传文件的支持:

1
2
3
4
5
6
复制代码@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(1000000);
return multipartResolver;
}
  1. 控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
复制代码package org.light4j.springMvc4.web;

import java.io.File;
import java.io.IOException;

import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller
public class UploadController {

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody
String upload(MultipartFile file) {// ①

try {
FileUtils.writeByteArrayToFile(new File("e:/upload/" + file.getOriginalFilename()),file.getBytes()); // ②
return "ok";
} catch (IOException e) {
e.printStackTrace();
return "wrong";
}
}
}

代码解释:

① 使用MultipartFile file接受上传的文件。
② 使用FileUtils.writeByteArrayToFile快速写文件到磁盘。

  1. 运行

访问http://localhost/springMvc4.x-fileUpload/toUpload,效果如下图所示:
xxx
单击”上传”按钮之后,上传文件之后页面显示ok,如下图所示:

查看f:/upload/文件夹下面增加了刚刚上传的文件,如下图所示:
xxx

三. 源代码示例:

github地址:点击查看
码云地址:点击查看

打赏 欢迎关注人生设计师的微信公众账号
公众号ID:longjiazuoA

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

【Java并发系列】 2Java中的原子操作类

发表于 2017-11-12

系列目录

  • [Java并发系列] 1.Java并发机制的底层实现
  • [Java并发系列] 2.Java中的原子操作类

1. 原子操作类的作用

当程序更新一个变量时,如果多个线程同时更新该变量,可能会得到期望以外的值。比如i=1, 线程A更新i+1, 同时线程B更新I+1,经过两个线程的操作,最终变量i的值可能不是3,而是2。因为线程A、B拿到的i的值都是1,这就是线程不安全的更新操作。我们可以用synchronized来解决这样的问题,synchronized可以保证多线程之间的同步,以保证多个线程不会同时操作变量i。
但是在JDK1.5开始,就提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了更为简单高效、线程安全的方式来更新一个变量的值。

2. 原子操作类基本分类
  • 原子更新基本类型(3个)
  1. AtomicBoolean 原子更新布尔类型
  2. AtomicInteger 原子更新整型
  3. AtomicLong 原子更新长整型
  • 原子更新数组(3个)
  1. AtomicIntegerArray 原子更新整形数组中的元素
  2. AtomicLongArray 原子更新长整型数组中的元素
  3. AtomicReferenceArray 原子更新引用类型数组中的元素
  • 原子更新引用类型(3个)
  1. AtomicReference 原子更新引用类型
  2. AtomicReferenceFieldUpdater 原子更新引用类型中的字段
  3. AtomicMarkableReference 原子更新带有标记位的引用类型
  • 原子更新字段类(3个)
  1. AtomicIntegerFieldUpdater 原子更新整形字段
  2. AtomicLongFieldUpdater 原子更新长整型字段
  3. AtomicStampedReference 原子更新带有版本号的引用类型
3. CAS方式实现原子操作基本原理

JVM中CAS操作主要是利用了处理器提供的CMPXCHG执行实现。基本的思路就是利用循环进行CAS操作,直到成功为止。CAS主要涉及到三个操作数,内存中的值(V)、旧的预期值(A)、需要修改的新值(B),当且仅当V==A时,才会将V值修改为B值,否则什么都不做,并且通过一个布尔值返回结果。伪代码如下:

1
2
3
4
5
6
7
复制代码//伪代码
boolean compareAndSwap(V,A,B){
for(;;){
if(V==A)
V=B;//替换旧值
}
}
4. CAS方式产生的问题(3个)
  1. ABA问题: CAS操作时,检查值有没有变化,如果没有变化则更新,但是如果一个值原来是A,中间变成了B,然后又变为A,CAS进行检查时,就会发现它的值没有变化,但是实际上却已经变化了。解决ABA问题,可以在变量前加一个版本号,变量更新时,版本号就加1.
  2. 循环时间长,开销大:CAS采用的是自循的方式进行检查,如果长时间不成功,那么就会给CPU带来非常大的开销。
  3. 只能保证一个共享变量的原子操作:当对一个共享变量进行原子操作时,我们可以采用CAS的方式进行更新,但是如果对多个共享变量进行操作时,CAS就无法保证操作的原子性,那么这个时候就需要用锁来实现。
5. 原子操作类中主要的方法
  • boolean compareAndSet(int expect, int update) ;如果输入的值等于预期值,那么以原子的方式将该值设为输入的值。
  • int addAndGet(int delta);以原子的方式将输入的数值与实例中的值相加,并返回更新之后的值
  • int getAndAdd(int delta); 以原子的方式将输入的数值与实例中的值相加,并返回旧值
  • int getAndSet(int newValue);以原子方式设置为newValue的值,并返回旧值

通过阅读源码,可以发现CAS操作都是使用Unsafe类下的方法进行操作,而Unsafe类只提供了三种CAS方法:

  • compareAndSwapObject(this, valueOffset, expect, update);
  • compareAndSwapLong(this, valueOffset, expect, update);
  • compareAndSwapInt(this, valueOffset, expect, update);
    所以,对于其他类型的原子操作,都是进行类型转换,将其类型转换为这三种类型,然后进行原子操作。如Boolean型的,先转成整整,然后在使用compareAndSwapInt进行操作;所以像char/float/double/short…等都可以按照这种思路实现。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

NGINX下运行静态资源、PHP应用及支持HTTPS等配置详

发表于 2017-11-12

本文主要记录了当前博客下针对静态资源、旧站301跳转、Https配置等一系列内容,nginx以server块来确定某一部分虚拟域名及相关配置,所以我们可以在server块中配置server_name虚拟域名,access_log访问日志,return跳转,root项目根目录,location匹配url做相应操作,error_page错误页面,listen监听端口,include包含配置文件以及其他的一些ssl等操作,下面总结一下当前所使用内容

301&302跳转

原有旧站blog.congcong.us等都301跳转到www.congcong.us

$scheme为当前的协议
$request_uri为请求参数
配置代码如下:

1
2
3
4
5
6
复制代码server {
server_name congcong.us blog.congcong.us;
access_log /var/log/nginx/www.access.log;
#root /usr/share/nginx/html/blogtemp;
return 301 $scheme://www.congcong.us$request_uri;
}

静态资源配置缓存

配置图片及css等内容根据需要进行缓存 针对图片的请求 access_log不进行记录 expires为过期时间

1
2
3
4
5
6
7
8
9
复制代码location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
access_log off;
expires 30d;
}

location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}

配置php,及配置php的url美化

过滤所有的url 如果说非以index.php结尾,那么增加这个进行rewrite

过滤所有的php结尾内容 转交由php-fpm进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
复制代码location / {

if (-f $request_filename/index.html){
rewrite (.*) $1/index.html break;
}
if (-f $request_filename/index.php){
rewrite (.*) $1/index.php;
}
if (!-f $request_filename){
rewrite (.*) /index.php;
}
}

location ~ \.php$ {
root /usr/share/nginx/html/wordpress;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 1000;
include fastcgi_params;
}

配置Https,进行SSL配置

监听443端口,ssl配置开启,关联crt与key,设置ssl协议,加密算法支持等内容(腾讯云申请的免费证书)

1
2
3
4
5
6
7
8
复制代码listen 443;
ssl on;
ssl_certificate "/xxx/xxx/1_www.congcong.us_bundle.crt";
ssl_certificate_key "/xxx/xxx/2_www.congcong.us.key";
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;

完整配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
复制代码#
# The default server
#
server {
server_name congcong.us blog.congcong.us;
access_log /var/log/nginx/www.access.log;
return 301 $scheme://www.congcong.us$request_uri;
}

server {
server_name projects.congcong.us kindle.congcong.us;
access_log /var/log/nginx/pk.access.log;
return 302 $scheme://congcong.us;
}


server {
listen 443;
server_name www.congcong.us;
root /xxx/xxx/xxx/xxx/wordpress;
index index.php index.html index.htm;

# Load configuration files for the default server block.
include /xxx/xxx/default.d/*.conf;

location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
access_log off;
expires 30d;
}

location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}


location / {

if (-f $request_filename/index.html){
rewrite (.*) $1/index.html break;
}
if (-f $request_filename/index.php){
rewrite (.*) $1/index.php;
}
if (!-f $request_filename){
rewrite (.*) /index.php;
}
}

location ~ \.php$ {
root /xxx/xxx/xxx/xxx/wordpress;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 1000;
include fastcgi_params;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}

ssl on;
ssl_certificate "/xxx/xxx/1_www.congcong.us_bundle.crt";
ssl_certificate_key "/xxx/xxx/2_www.congcong.us.key";
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;

}

server {
listen 80 default_server;
server_name www.congcong.us;
root /xxx/xxx/xxx/xxx/wordpress;
index index.php index.html index.htm;

# Load configuration files for the default server block.
include /xxx/xxx/default.d/*.conf;

location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
access_log off;
expires 30d;
}

location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
access_log off;
expires 24h;
}


location / {

if (-f $request_filename/index.html){
rewrite (.*) $1/index.html break;
}
if (-f $request_filename/index.php){
rewrite (.*) $1/index.php;
}
if (!-f $request_filename){
rewrite (.*) /index.php;
}
}
location ~ \.php$ {
root /xxx/xxx/xxx/xxx/wordpress;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_read_timeout 1000;
include fastcgi_params;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}

相关文章

  • [NOTE-C]C语言指针详解(一)
  • [NOTE-PHP]PHP依赖管理工具Composer详解
  • [NOTE-ENCRYPT]Java实现非对称加密RSA、DH算法
  • [NOTE-ENCRYPT]Java实现RSA、DSA、ECDSA算法的签名与验签
  • [NOTE-ENCRYPT]非对称加密算法与RSA详解
  • [NOTE-DS]数据结构概述与大O记号
  • [NOTE-C]C语言输入与输出
  • [NOTE-C]C语言运算符

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SegmentFault 技术周刊 Vol36 - 一篇入

发表于 2017-11-12

一篇带你入门 Spring Boot。

Spring Boot 初识

SpringBoot前世今生

本文主要讲述spring boot的由来,即其它诞生的背景,初衷,现状,及对未来的展望。

Spring Boot参考指南中文版–Chapter1.Spring Boot中文文档

本节提供一个Spring Boot参考文档的简明概述。你可以把它作为文档其余部分的导航。你可以从头到尾依次阅读该参考指南,或跳过你不感兴趣的章节。

Spring Boot 学习资料收集

Spring Boot QuickStart

Spring Boot QuickStart (1)

Spring Boot 简化了基于 Spring 的应用开发,你只需要 “run” 就能创建一个独立的,产品级别的 Spring 应用。

Spring 平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数 Spring Boot 应用只需要很少的Spring 配置。你可以使用 Spring Boot 创建 Java 应用,并使用 java -jar 启动它或采用传统的 war 部署方式。

  • 系统要求
  • 安装
  • 使用
  • 配置

Spring Boot QuickStart (2) - 基础

基于 Spring Boot 创建一个命令行应用,先来个最基本的体验,体验一下:

  • 配置管理(配置文件加载,多环境配置文件)
  • 日志
  • 单元测试

Spring Boot QuickStart (3) - Web & Restful

基于 Spring Boot 可以快速创建一个Web & Restful 应用。

  • 注解
  • 路由,方法
  • 请求参数
  • Cookie
  • Session
  • 模板引擎
  • 常用配置

Spring Boot QuickStart (4) - Database

到了操作数据库的环节,以 MySQL 为基准,体验一下数据库的相关操作,JPA、MyBatis 将是学习重点。体验的基线:

  • 单表。增、删、改、查(多条件组合查询、分页,排序等)
  • 多表关联。一对一,一对多,多对多

Spring Boot QuickStart (5) - Spring Data JPA

Java Persistence API,可以理解就是 Java 一个持久化标准或规范,Spring Data JPA 是对它的实现。并且提供多个 JPA 厂商适配,如 Hibernate、Apache 的 OpenJpa、Eclipse的EclipseLink等。

spring-boot-starter-data-jpa 默认使用的是 Hibernate 实现。

在 SpringBoot + Spring Data Jpa 中,不需要额外的配置什么,只需要编写实体类(Entity)与数据访问接口(Repository)就能开箱即用,Spring Data JPA 能基于接口中的方法规范命名自动的帮你生成实现(根据方法命名生成实现,是不是很牛逼?)

Spring Boot 入门实用教程

Spring Boot - 整合Jsp/FreeMarker

本文讲述了(json,jsp,freemarker)配置及整合方法,并针对web开发常用的注解的概念及功能进行了介绍,留下了一个疑问:为什么整合jsp后必须通过spring-boot:run方式启动?欢迎大家留言讨论。

Spring Boot - Servlet、过滤器、监听器、拦截器

本文讲解了注册Servlet/Filter/Listener的两种⽅方式(Servlet/Filter/Listener的概念大家自行查阅资料了解),及拦截器基本原理,并通过注解实现http拦截器,另外本文还有一个疑问:为什么Spring中实现的Http拦截器,无法对我们自定义的servlet请求进行拦截?欢迎大家留言讨论。

Spring Boot - 静态资源处理、启动加载、日志处理

  1. 静态资源处理
1. 默认资源映射(/\*\* - > /resources/static) ,重点是默认目录的优先级
2. ⾃定义资源映射 (继承WebMvcConfigurerAdapter 并重写方法addResourceHandlers)
  1. 启动加载
1. CommandLineRunner (实现接⼝,多个类加载的优先级)
  1. 日志处理
1. logback(配置,控制台输出,文件输出)

Spring Boot - 整合JdbcTemplate、MyBatis

  • 本文讲解Spring Boot整合JdbcTemplate、整合mybatis,介绍并重点讲解了事务处理和配置。
  • 本文未提及关于分⻚查询和数据库连接池,我认为分页查询的重点是分页算法如何封装,并不是Spring Boot关注的重点,大家可以自己实现。
  • 另外现在常用的数据库连接池有c3p0/dbcp/tomcat-jdbc/HikariCP。
  • 顺便提一下,在Spring Boot中更改数据源,只需要在application.properties配置文件中增加spring.datasource.type配置即可。

Spring Boot - 部署Deploy

服务发布Tomcat:

  • 修改启动类,继承 SpringBootServletInitializer 并重写 configure 方法
  • 修改pom文件中jar 为 war
  • 修改pom,排除tomcat插件
  • 打包部署到容器

Spring Boot 属性配置

SpringBoot配置属性之MVC

SpringBoot配置属性之Server

SpringBoot配置属性之DataSource

SpringBoot配置属性之NOSQL

SpringBoot配置属性之MQ

SpringBoot配置属性之Security

SpringBoot配置属性之Migration

SpringBoot配置属性之其他

SpringBoot配置文件日期属性转换实例

Spring Boot 进阶

Spring Boot - 自定义启动banner

实现的方式非常简单,我们只需要在Spring Boot工程的/src/main/resources目录下创建一个banner.txt文件,然后将ASCII字符画复制进去,就能替换默认的banner了。

spring boot validated的使用

spring-boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。

比如,我们判断一个输入参数是否合法,可以用如下方式

spring-boot启动初探

Spring Boot充分利用了JavaConfig的配置模式以及“约定优于配置”的理念,能够极大的简化基于Spring MVC的Web应用和REST服务开发。

使用spring boot开发web应用,决定项目是否可以直接启动的是spring-boot-starter-tomcat模块,我们可以直接引入spring-boot-starter-web。

Maven管理SpringBoot Profile

完成了上面的五步,即可使项目根据你的构建参数的不同,打包出不同环境下运行的包。

  1. 第1步去掉了SpringBoot内嵌的tomcat和tomcat-jdbc。使得我们可以决定在什么情况下使用何种容器运行我们的项目。
  2. 第2步配置了Maven构建Porfile,使得构建可根据我们的指令分发不同的包。
  3. 第3步配置了Maven资源过滤,不仅使得不同Profile下的资源文件互不可见,且替换了资源文件中以“@xx@”表示的属性值。
  4. 第4步使Spring的Profile由Maven决策,这样,我们就不用每次打包都修改Spring的Profile配置了。
  5. 第5步展示了如何执行不同Profile下的构建命令,并且使用了一个Shell脚本方便我们执行构建和跳过测试(多数时候我们在构建项目时先测试,并不需要在构建时测试,测试和构建的解耦使得我们更专注。但同时,如果你忘记了前置测试,也可能会引发未察觉的测试问题)。

SpringBoot四大神器之Actuator

Spring Boot有四大神器,分别是auto-configuration、starters、cli、actuator,本文主要讲actuator。actuator是spring boot提供的对应用系统的自省和监控的集成功能,可以对应用系统进行配置查看、相关功能统计等。

SpringBoot四大神器之Starter

SpringBoot的starter主要用来简化依赖用的。本文主要分两部分,一部分是列出一些starter的依赖,另一部分是教你自己写一个starter。

SpringBoot RESTful 应用中的异常处理小结

  • @ControllerAdvice 和 @ExceptionHandler 的区别
  • 处理 Controller 中的异常
  • 处理 404 错误

Spring Boot整合jsp后必须通过spring-boot:run方式启动?

简单总结一下,本文阐述的问题并不是日常开发中的主要问题(可能连主要问题都算不上,谁会用main去调试??),但是遇到了就花时间来研究一下,还是有所收获的。

  • 分析问题思路
  • Spring Boot 初始化的部分流程
  • 请求转发和重定向的区别

另外大家注意如果pom文件中去掉,再正常部署到tomcat容器中,会有jar冲突,建议大家试验过后,修改回去。

Spring-boot 启动时碰到的错误

通过springBoot构建一个简单的Restful webService

springboot定制404错误信息

SpringBoot-vue 基于Java的微服务全栈快速开发实践

Spring Boot 讲堂

Java 微服务实践 - Spring Boot 系列

Java 微服务实践 - Spring Boot 为系列讲座,二十节专题直播,时长高达50个小时,包括目前最流行技术,深入源码分析,授人以渔的方式,帮助初学者深入浅出地掌握,为高阶从业人员抛砖引玉。

系列讲座列表:

1. Java 微服务实践 - Spring Boot 系列(一)初体验

2. Java 微服务实践 - Spring Boot 系列(二) Web篇(上)

3. Java 微服务实践 - Spring Boot 系列(三)Web篇(中)

4. Java 微服务实践 - Spring Boot 系列(四)Web篇(下)

5. Java 微服务实践 - Spring Boot 系列(五)嵌入式Web容器

6. Java 微服务实践 - Spring Boot 系列(六)数据库 JDBC

7. Java 微服务实践 - Spring Boot 系列(七)MyBatis

8. Java 微服务实践 - Spring Boot 系列(八)JPA

9. Java 微服务实践 - Spring Boot 系列(九)NoSQL

10. Java 微服务实践 - Spring Boot 系列(十)缓存

11. Java 微服务实践 - Spring Boot 系列(十一)消息

12. Java 微服务实践 - Spring Boot 系列(十二)验证

13. Java 微服务实践 - Spring Boot 系列(十三)WebSocket

14. Java 微服务实践- Spring Boot 系列(十四)WebService

15. Java 微服务实践 - Spring Boot 系列(十五)安全

16. Java 微服务实践 - Spring Boot 系列(十六)日志

17. Java 微服务实践 - Spring Boot 系列(十七)监管

18. Java 微服务实践 - Spring Boot 系列(十八)配置

19. Java 微服务实践 - Spring Boot 系列(十九)测试

20. Java 微服务实践 - Spring Boot 系列(二十)自定义启动器

插播一则消息:11.11 - 11.13,SF 讲堂将开启优惠模式,大部分讲座均有不同限量的优惠,等你来抢! >>> 电梯直达活动页

本期完:)


欢迎关注 SegmentFault 微信服务号,获取最新讲堂及优惠信息。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

学习 Lumen 用户认证 (二) —— 使用 jwt-au

发表于 2017-11-12

通过上一篇《学习 Lumen 用户认证 (一)》mp.weixin.qq.com/s/KVUQE2DUe…的学习,大致懂了 Lumen 的用户认证主要使用 「api」的方式,来默认进行用户认证:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
复制代码<?php

namespace App\Providers;

use App\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}

/**
* Boot the authentication services for the application.
*
* @return void
*/
public function boot()
{
// Here you may define how you wish users to be authenticated for your Lumen
// application. The callback which receives the incoming request instance
// should return either a User instance or null. You're free to obtain
// the User instance via an API token or any other method necessary.

$this->app['auth']->viaRequest('api', function ($request) {
if ($request->input('api_token')) {
return User::where('api_token', $request->input('api_token'))->first();
}
});
}
}

当然在实际开发中,我们不能只是简单的获取 api_token直接关联数据库查找用户信息。

在 API 开发中,用户认证是核心,是数据是否有保障的前提,目前主要有两种常用方式进行用户认证: JWT 和 OAuth2。

本文将简要说说如何利用 JWT 来进行用户认证

JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON 的开放标准 (RFC 7519)。该 token 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

关于 JWT 更具体的介绍,相信网上有很多帖子和文章值得参考,这里先不阐述了。

为了学习 JWT 在 Lumen 中的使用,最好的办法就是在「程序员同志网 —— GitHub」搜索有关插件,找个 stars 最多的那个拿来研究研究。

tymondesigns/jwt-auth

JSON Web Token Authentication for Laravel & Lumen

安装 jwt-auth

通过 Composer 安装:

1
复制代码composer require tymon/jwt-auth:"^1.0@dev"

注: 0.5.* 版本未对 Lumen 专门做封装

将 $app->withFacades() 和 auth 认证相关的注释去掉:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
复制代码<?php

require_once __DIR__.'/../vendor/autoload.php';

try {
(new Dotenv\Dotenv(__DIR__.'/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| Here we will load the environment and create the application instance
| that serves as the central piece of this framework. We'll use this
| application as an "IoC" container and router for this framework.
|
*/

$app = new Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);

// 取消注释,这样就可以通过 Auth::user(),获取当前授权用户
$app->withFacades();

$app->withEloquent();

/*
|--------------------------------------------------------------------------
| Register Container Bindings
|--------------------------------------------------------------------------
|
| Now we will register a few bindings in the service container. We will
| register the exception handler and the console kernel. You may add
| your own bindings here if you like or you can make another file.
|
*/

$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);

$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);

/*
|--------------------------------------------------------------------------
| Register Middleware
|--------------------------------------------------------------------------
|
| Next, we will register the middleware with the application. These can
| be global middleware that run before and after each request into a
| route or middleware that'll be assigned to some specific routes.
|
*/

// $app->middleware([
// App\Http\Middleware\ExampleMiddleware::class
// ]);

// 增加 auth 中间件
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

/*
|--------------------------------------------------------------------------
| Register Service Providers
|--------------------------------------------------------------------------
|
| Here we will register all of the application's service providers which
| are used to bind services into the container. Service providers are
| totally optional, so you are not required to uncomment this line.
|
*/

$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
// $app->register(App\Providers\EventServiceProvider::class);

/*
|--------------------------------------------------------------------------
| Load The Application Routes
|--------------------------------------------------------------------------
|
| Next we will include the routes file so that they can all be added to
| the application. This will provide all of the URLs the application
| can respond to, as well as the controllers that may handle them.
|
*/

$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});

return $app;

然后在 AppServiceProvider 中注册 LumenServiceProvider:

1
复制代码$this->app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);

在 Lumen 项目中,默认没有 config 文件夹,需要在项目根目录创建,并将 vendor 源代码中auth.php 复制出来,同时将 api 认证指定为「jwt」:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
复制代码<?php

return [

/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/

'defaults' => [
'guard' => env('AUTH_GUARD', 'api'),
],

/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
|
*/

'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users'
],
],

/*
|--------------------------------------------------------------------------
| User Providers
|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/

'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\User::class,
],
],

/*
|--------------------------------------------------------------------------
| Resetting Passwords
|--------------------------------------------------------------------------
|
| Here you may set the options for resetting passwords including the view
| that is your password reset e-mail. You may also set the name of the
| table that maintains all of the reset tokens for your application.
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
*/

'passwords' => [
//
],

];

最后,因为 JWT 协议需要用到 secret,所以需要生成一个 secret:

1
复制代码php artisan jwt:secret

使用 jwt-auth

1. 更新 User Model

继承 Tymon\JWTAuth\Contracts\JWTSubject:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
复制代码<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Laravel\Lumen\Auth\Authorizable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Model implements AuthenticatableContract, AuthorizableContract, JWTSubject
{
use Authenticatable, Authorizable;

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email',
];

/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'password',
];

/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}

/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}

2. 写一个 Login 方法,验证登陆信息,并返回 token 回客户端:

1
2
复制代码// 路由
$router->post('/auth/login', 'AuthController@postLogin');

postLogin 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
复制代码<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Tymon\JWTAuth\JWTAuth;

class AuthController extends Controller
{
protected $jwt;

public function __construct(JWTAuth $jwt)
{
$this->jwt = $jwt;
}

public function postLogin(Request $request)
{
if (! $token = $this->jwt->attempt($request->only('email', 'password'))) {
return response()->json(['user_not_found'], 404);
}

return response()->json(compact('token'));
}
}

可以请求试试了,用 Postman 跑跑:

有了 token 了。我们就可以用来测试,看能不能认证成功,获取用户信息。

3. 使用 token 获取用户信息

1
2
3
4
5
复制代码// 使用 auth:api 中间件
$router->group(['middleware' => 'auth:api'], function($router)
{
$router->get('/test', 'ExampleController@getUser');
});

只要验证通过,就可以利用 Auth:user()方法获取用户信息了。

1
2
3
复制代码public function getUser(Request $request) {
return response()->json(['user' => Auth::user()]);
}

对照数据库:

以后只要在请求的 headers 中加入 token 信息即可,完美实现用户认证。

想了解有关 Lumen 的认证相关内容,可以参考上一篇文章《学习 Lumen 用户认证 (一)》mp.weixin.qq.com/s/KVUQE2DUe…

也可以参考 Lumen 官网
lumen.laravel-china.org/docs/5.3/au…

总结

对获取到 token 值 (eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vZGVtby5hcHAvYXV0aC9sb2dpbiIsImlhdCI6MTUxMDQ3NTQ5MiwiZXhwIjoxNTEwNDc5MDkyLCJuYmYiOjE1MTA0NzU0OTIsImp0aSI6Imx3UFpSMTN0MlV5eXRib1oiLCJzdWIiOjEsInBydiI6Ijg3ZTBhZjFlZjlmZDE1ODEyZmRlYzk3MTUzYTE0ZTBiMDQ3NTQ2YWEifQ.YTvsiO9MT3VgPZiI03v2sVEIsGLj8AUwJiDuXvCAvHI) 仔细观察,就会发现中间是由两个「.」来合并三段信息的。

下一步我们就来研究研究 JWT 的原理和也可以自己动手写个基于 JWT 的 Lumen 认证插件出来。

「未完待续」


coding01 期待您继续关注

qrcode

qrcode


也很感谢您能看到这了

qrcode

qrcode

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…948949950…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%