Java Synchronized 关键字

Synchronized是基于monitor实现的,Synchronized经过编译后,会在同步块前后分别形成monitorenter和monitorexit两个字节码指令,在执行monitorenter指令时,首先要尝试获取对象锁,如果对象没有别锁定,或者当前已经拥有这个对象锁,把锁的计数器加1,相应的在执行monitorexit指令时,会将计数器减1,当计数器为0时,锁就被释放了。
如果获取锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

synchronized底层语义原理

Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。
同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的,关于这点,稍后详细分析。

下面先来了解一个概念Java对象头,这对深入理解synchronized实现原理非常关键。

synchronized特点:可重入锁/排他锁/属于jvm,由jvm实现
可重入锁实现可重入性原理或机制是:
每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;
当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。

synchronized保证可见性
JMM关于synchronized的两条规定:
1)线程解锁前,必须把共享变量的最新值刷新到主内存中
2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。

在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。

实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。
Java头对象,它实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成

轻量级锁和偏向锁是Java 6 对 synchronized 锁进行优化后新增加的,重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。
每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //记录个数
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时。
首先会进入 _EntryList 集合,当线程获取到对象的monitor后进入_Owner区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。
若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。

monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因

Makefile基本结构

基本格式

target ... : prerequisites ...
	command
	...
	...

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。值得注意的是,在 Makefile 中的命令,必须要以[Tab]键开始。

变量定义与预定义变量

对于在Makefile中重复出现的文件,可以将其自定义成我们所命名的变量。在Makefile中变量一般都是字符串,有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

在Makefile中有许多变量预先就定义好了,可供我们直接调用。常用的预定义变量:
$* 不包含扩展名的目标文件名称。
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
$< 第一个依赖文件的名称。
$? 所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
$@ 目标的完整名称。
$^ 所有的依赖文件,以空格分开,不包含重复的依赖文件。
$% 如果目标是归档成员,则该变量表示目标的归档成员名称。

使用通配符

在Makefile的编写中我们也可以用到我们平时使用到的通配符。如:星号代表任意个任意字符,*.o代表文件夹中所有.o文件。

显示规则与注释

make 会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被 make 显示出来。

如果 make 执行时,带入 make 参数“-n”或“–just-print”,那么其只是显示命令,但不会执行命令,这个功能可以用来调试我们的Makefile,观察我们编写的Makefile的执行顺序。而 make 参数“-s”或“–slient”则是全面禁止命令的显示。

Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,类似于C/C++中的“//”一样。

隐晦规则

在我们使用 Makefile 时,有一些我们会经常使用,而且使用频率非常高的东西,make其实是预先规定好的,其具有自动推导的功能,它可以自动推导文件以及文件依赖关系后面的命令,如.o文件由.c文件编译而来,它们之间的依赖关系是预先就设定好的,于是我们就没必要去在每一个.o文件后都写上类似的命令

Makefile可以嵌套执行,在大型的工程项目中,我们将不同的模块放在不同的文件夹,然后为每个文件夹编写相应的Makefile,有利于我们Makefile的维护。

网络硬件卸载

OpenStack的Pike版本中引入了对switchdev的支持,实现了Open vSwitch硬件卸载offloading功能。本文简介一下网络硬件卸载。

网络Offload:

说起网络offload,主要是指将原本在内核网络协议栈中进行的IP分片、TCP分段、重组、checksum校验等操作,转移到网卡硬件中进行,CPU的发包路径更短,消耗更低,提高处理性能。

一开始这些offload功能都是在网卡上针对特定功能设计一个专门的电路并且带有很小的缓存,去做专门的事情。后来直接在网卡上部署一个可编程的通用的小型CPU,一般称为网络协处理器。就是现在的智能网卡。智能网卡的协处理器可以先对该数据包进行一些预处理,根据处理结果考虑是不是要把数据包发送给主机CPU,智能网卡中的offload功能一般是使用eBPF编程来实现的。

交换offload:

Linux 4.0引入了一个switchdev框架,它代表一类拥有“交换”能力芯片的多网口设备的抽象。其中每一个网口就是一个port,在switchdev框架中被注册成一个net_device。

switchdev起源于Open vSwitch项目,由Jiři Pirko在2014年9月首次提出。在2015年2月的Netdev 0.1会议上,网络开发人员决定扩展并采用switchdev作为硬件交换机芯片的通用解决方案。switchdev驱动模型出现之前,Linux需要交换机厂商的专门工具套件操作交换机,而在switchdev驱动模型之后,通用接口被实现,交换机正式纳入Linux网络设备体系,Linux可以用标准接口实现交换机的控制面和管理面。

架构:

在switchdev驱动框架下,硬件交换机设备上的每个物理端口都在内核中注册为一个net_device,就像对现有的网络接口卡(nic)所做的那样。可以使用现有的工具(如桥接、ip和iproute2)将端口绑定或桥接、隧道化或划分vlan。switchdev驱动程序的优点是这样的交换结构可以被卸载到交换机硬件上。因此,驱动程序将转发数据库(FDB)中的每个条目镜像到硬件,并监视其更改情况。

内核中switch架构图如下:

最初,switchdev支持的唯一设备是QEMU的“rocker”软件交换机。后来Mellanox和Broadcom等公司均提供了支持switchdev的交换机器。

OpenStack Pike版本中引入了对switchdev的支持,实现了Open vSwitch硬件卸载offloading功能。

OpenStack官方文档关于网络offload部分描述:

Supported Ethernet controllers:

The following manufacturers are known to work:

Mellanox ConnectX-4 NIC (VLAN Offload)

Mellanox ConnectX-4 Lx/ConnectX-5 NICs (VLAN/VXLAN Offload)

Prerequisites:

Linux Kernel >= 4.13

Open vSwitch >= 2.8

iproute >= 4.12

Mellanox NIC

面对职场的流言蜚语,如何正确应对

大家都是普通人,流言的困扰想必大家都经受过。办公室里是非多,时不时就传出莫名其妙的流言来,和经理出差几天回来就发现在同事眼里,你和经理已经是暧昧关系了;生病请假几天,就马上有人传言你快要跳槽了,捕风捉影,不一而足。对大多数白领而言,工作中影响心绪最多的莫过于听到这类与自己有关的流言的。

一场疫情的侵袭,让原本的氛围波澜不断,公司的不断裁员,让有些人动了歪心思,恶意制造流言,企图用伤害别人的方式保全自己,当事情发生在我们身上时候又该如何面对?

职场太无情,当你被这些流言蜚语中伤,有许多人正想看你气急败坏的失态状,并以此再次诽谤你。

面对风起云涌的职场,我们如何做到波澜不惊,内心平静不受影响,还能正确面对这些流言蜚语,避免让自己被中伤呢?坚持一些自己的原则,或许可以让你轻松一些应对。

1、不做气急败坏的冲动举动。

想要害你的人,想要中伤你的人,捏造关于你的流言蜚语的目的,也就是为了让你形象败坏,让你当众出丑,下不了台。

如果你正是因为这样的恶语,就气急败坏,生气做出一些冲动的举动,那刚巧就中了他们的圈套。

因此,在这种情况下,一定要控制自己内心的怒火,不要做出气急败坏影响自己形象的举动。

2、不做同等手段的报复。

有些人被流言蜚语诽谤,为了以牙还牙,采取同样的手段加以打击报复,而在这个报复的过程中,正是把自己从白洗到黑,反而坐实了流言。

所以,如此一来,想要清者自清的,反而是失去了自己的优势。

3、调整心态,让他们去说吧。

流言蜚语的最终目的,就是要扰乱你的阵脚,让你在工作中迷失心志,最后颇有居心的人赢得了胜利。

当我们遭受流言攻击,一定要调整心态,不要过于在乎别人的眼光和言语,看清自己的所作所为,让他们去说吧!

4、等待时间证实自己。

面对他人的流言蜚语,我们再解释,可能只会越抹越黑,无济于事反而让自己陷入更坏的境地。因此,我们要做的,恐怕也只有静待时间来洗白自己的冤屈。清者自清,在你的实际表现中,别人会看到你真实的一面,时间会证实一切。

5、职场无朋友

不要总试图和职场同事做朋友,或许大家可以和谐相处,谈笑风声;但毕竟大家是合作关系,团队有绩效,在利益面前大家又是竞争关系,很多时间不需要委屈自己来迎合他人; 职场中交友和人生中相似,古往今来都是人生得一知己足已,何况是竞争之中的职场,朋友是可遇不可求,身在职场之中我们主要的目的是用自己的劳动换取收入,这是我们劳动者的权利,没有必要委屈求全,做好自己的工作.如果环境不好我们可以更换更适合自己的环境.

职场之中大家为了各自的利益聚在一起,共同做一些事情,在人群之中能有那么一两个朋友就更好了,没有也莫自噫,守好本心,丰富完善自己,当自己的层次提升后,很多问题也许就自然解决了.

十年加老程序员给新手的几条忠告

要坚持使用一个主要开发平台,框架或语言

一旦你找到一个让你觉得舒服的平台,框架或语言,就应该坚持下去。从长远看,你期望的薪水和你的工作能力都会比频繁跳槽来得更高。找到最需要你的专属技能的好公司,然后踏实地在那里工作。我知道这听起来很简单,但我花了很多年才意识到这点。

不要为高工资追求管理角色

如果你对敲代码感到厌烦了,那么转成管理岗并不是解决方案,更好的办法是横向移动,换一个部门,换一家公司,或者是旅行,甚至可以选择在国外工作来解放你的思想,在全世界有许多技术中心,那里对开发者的要求很高,这能帮助你打开眼界。

休息一段时间

重新找回工作是很容易的,比预期的要容易得多。所以,如果你觉得自己需要的话,不要害怕一个长时间的休假。简历中的一段空白对于开发者来说不会被人认为有错。

1、保持编码( Keep coding continuously)

从长远来看,拥有个人项目、阅读、写博客和参加访谈都会有助于你成为一个更好的开发人员。只要你觉得有意思,就把你的代码推到 GitHub,并把你的经验与社区分享。

2、成为开发者社区的一员

同行聚会和进入开发群是很有趣的事情,是结识开发者伙伴的好地方。如果你想在这个行业里做到最好,就尽量不要消极被动。与坐在你旁边的人交谈,提供你的帮助,分享你的经验。每个人都喜欢谈论自己的经历,更何况你也许会遇到很多潜在的雇主。

乐意和其他开发者闲聊交谈,在那里你会碰到许多需要教练的初学者团体,你可以指导新手,给他们提供练手的机会,甚至赞助活动。

要学习别人的开发技巧并且勇于实践,要热情地欢迎不同意见的人讲话,勇敢地听取他们的意见。

3、不要在招聘网站上求职

这是智慧的结晶:不要申请那些在网站上张贴的工作职位,很多前辈大咖都在这样干

招聘网站的背后是复杂的人力资源流程、候选人跟踪系统、滥发个人简历和混乱的沟通,这也是许多公司找不到人才而要依靠猎头公司的几个重要原因。

优秀的人才应该跳过这些。网络是寻找人才之路,更多地关注当地的社区消息,当有人离开一个重要的职位,当一个有意思的公司得到了融资,或者某个企业人事变动正在招聘人时,你都会获得最新的第一手消息。

你也能通过网络社交得知哪些公司的福利比较好,各公司的大多数开发者都会真诚对待你,他们会告诉你这些公司的架构是否老旧,经理是不是人品够好,甚至工资待遇怎么样。

如果你看到一个有意向的工作机会,首先看看你的社交网,看看你的用户组或 LinkedIn 朋友圈,是否有人已经在那里工作了,或者问他们是否认识某些人。然后直接与他们联系,与那个公司的 CTO、招聘经理或其他开发人员直接交谈。

猎头是好的,但不要依赖他们。他们的任务只是帮助企业填上一个职位的空缺,因为他们并不为你工作。如果他们推荐的职位的确非常适合你所寻找的工作,那么你就去做,如果觉得不适合,那么就告诉他们:谢谢,我不去。

血色浪漫-钟跃民身边的三个女人

说说这几个女人吧。最初的主角仅仅是为了显摆,才去撩周小白,周小白是个个性鲜明的女子,她的身上有一种小资情调,这在当时是很吸引年轻小伙的。就像现在品学兼优,家境富裕,如花似玉的中学生。最受男生的爱慕。但是,周小白却有这类女生共有的缺点,大小姐脾气重,支配欲很强,眼界颇高。对于这样的女生,除非有一个男生各方面都异常优秀,又能容忍她的大小姐脾气,不然这种女性的情感经历会比较坎坷。如果说钟跃民与周小白在脾气方面还能够磨合的话,两人在人生观的差别就相当大了,一个试图将自己的人生变得丰富多彩,另一个只想安安稳稳的守着自己的男人过踏实的生活。

第二个女人秦岭,这女人像女版的钟跃民,两人的人生观有些相似,这样的两个人最合适的关系只是知己情人,而不是伴侣。他们之间,只是因为这层原因才有交集。一对长久的伴侣,要有共同的语言,有可以兼容的喜好,但最不能少的,是相互之间的责任感。而这,恰恰是两人之间最缺乏的。鈡与秦之间的感情,起于相同的爱好,更多的是从对方身上看到了自己的影子。至于为何鈡一直苦苦追寻秦的下落,其根源在于,他们在高粱垛那一次。每个男人对自己的第一个女人都是难以忘怀的,就算那女人不是个良家也一样。当然,更多的,是钟跃民对陕北生活的一种怀念。

第三个女人,高玥。有个这样的女人,真的是主角人生当中最大的福气。鈡对高的情感,可能不是爱情,却至少是一种眷恋与依靠。如果说钟跃民是风筝,高玥就是是风筝线一头的轴。任风筝飞的再高,终要被这转轴收到手心,即使风筝喜欢追逐风的脚步。高玥是个相当聪明的女人,她非常懂得把握鈡的心思。她不苛求鈡的承诺,也不需要钟跃民的细心呵护,这可以让钟跃民没有那么多的思想包袱,这是她相比于周小白的决定性优势。她可以为钟跃民置办好柴米油盐的一切琐事,让钟跃民全身心的追求他那丰富多彩的人生。她就这样,悄无声息的在这男人心底留下了一层无形的羁绊。

这么说吧,3个女人,从背景,性格,前程分析吧

1.周小白,父亲是军区首长,别人稀罕的事情在她这里就跟没事一样,性格很直接,可能跟家里最小的女儿有关系,爸爸宠着,哥哥惯着,所以看待事情起点不一样,不贴近现实的理想派,压根不知道穷苦人的生活方式以及牵挂。
2.秦岭,单亲家庭女,跟母亲一起,从小自立,所以情商高,思考的也高,身不由己也多,个人的前程就如疾风中的落叶一样,随风而走不由自个,然个人又倔强。
3.高玥,普通工薪阶层女儿,哥哥是知识分子,家里也崇尚稳定生活,所以造就她对新鲜事物和有不屈从性格的人有天生的被吸引力,个性直接,不随盲流,吸收新思想快,但本质上又是单纯的。

所以,性格决定命运。
1.周小白,注定是钟跃民一生打心眼里最稀罕的女人,但是又是最不忍心拥有的女人,她带给了钟跃民青春期的憧憬,与青涩期的骚动,但是钟跃民以当时的思想,是认定了自己与周小白是没有未来的,即使有,也是不平等不自由的未来,所以他选择放弃,反而是宁可牺牲自己一生的爱情也要保全周小白一生的感情,所以周小白注定是钟跃民一生真正爱过的女人,在内心唯一触动过的女人。
2.秦岭,与其说是钟跃民最爱的女人,不如说是钟跃民一生最想照顾的女人,因为当年一起下陕北,都等同是同一阶层的儿女,前途也是持平的,没有高低贵贱的不平搭配,而钟跃民喜欢新鲜事物,对秦岭的信天游的这种艺术,也深深折服,所以与其说他最爱秦岭,不如说是爱上了秦岭造就又给予他的一种感觉,加上秦岭把身子给了他,他也打心眼里希望这辈子能照顾秦岭,他对秦岭所有的遗憾,都来源于对自己无法实行责任的遗憾,秦岭就成了他这一生永远无法拔出的刺。
3.高玥,她不是钟跃民所爱的女人,她平凡而又不普通,她的一切特性,都是弥补了钟跃民性格上的缺失,所以她虽然不是钟跃民所爱的女人,却又是钟跃民一生最适合的女人,她的存在反而造就了钟跃民思维与故事的延续。

对于钟跃民这样的人来说,岁月会磨平菱角,但是他所做到的,就是在岁月在执行磨平人生过程中,他却在岁月的锉刀上画上了浓墨重彩的一笔,而这就是他的人生。

英衮畅人谋文明固天启

始出尚书省诗 作者:谢朓

惟昔逢休明。
十载朝云陛。
既通金闺籍。
复酌琼筵醴。
宸景厌昭临。
昏风沦继体。
纷虹乱朝日。
浊河秽清济。
防口犹宽政。
餐荼更如荠。
英衮畅人谋。
文明固天启。
青精翼紫{车犬}。
黄旗映朱邸。
还覩司隶章。
复见东都礼。
中区咸已泰。
轻生谅昭洒。
趋事辞宫阙。
载笔陪旌棨。
邑里向疏芜。
寒流自清泚。
衰柳尚沉沉。
凝露方泥泥。
零落悲友朋。
欢娱燕兄弟。
既秉丹石心。
宁流素丝涕。
因此得萧散。
垂竿深涧底。

netty http protocol

netty http协议实现的抽象,对http实现是基于非servlet的异步 IO 实现。

请求request的抽象描述,response对象的抽象比较类似

HttpMethod:主要是对method的封装,包含method序列化的操作
HttpVersion: 对version的封装,netty包含1.0和1.1的版本
QueryStringDecoder: 主要是对url进行封装,解析path和url上面的参数。(Tips:在tomcat中如果提交的post请求是application/x-www-form-urlencoded,则getParameter获取的是包含url后面和body里面所有的参数,而在netty中,获取的仅仅是url上面的参数)
HttpHeaders:包含对header的内容进行封装及操作
HttpContent:是对body进行封装,本质上就是一个ByteBuf。如果ByteBuf的长度是固定的,则请求的body过大,可能包含多个HttpContent,其中最后一个为LastHttpContent(空的HttpContent),用来说明body的结束。
HttpRequest:主要包含对Request Line和Header的组合
FullHttpRequest: 主要包含对HttpRequest和httpContent的组合

request的流程处理,只需要在netty的pipeLine中配置HttpRequestDecoder和HttpObjectAggregator。

原理

1:如果把解析这块理解是一个黑盒的话,则输入是ByteBuf,输出是FullHttpRequest。通过该对象便可获取到所有与http协议有关的信息。
2:HttpRequestDecoder先通过RequestLine和Header解析成HttpRequest对象,传入到HttpObjectAggregator。然后再通过body解析出httpContent对象,传入到HttpObjectAggregator。当HttpObjectAggregator发现是LastHttpContent,则代表http协议解析完成,封装FullHttpRequest。
3:对于body内容的读取涉及到Content-Length和trunked两种方式。两种方式只是在解析协议时处理的不一致,最终输出是一致的。

性能优化:
1:使用堆外内存,也就是DirectBuffer。来减少GC的次数。
2:使用buffer pool,避免频繁的申请及释放内存。一般pool有两层,ThreadLocal的pool和全局的pool。 申请buffer空间时,先看ThreadLocal是否有未使用的buffer,如果没有,再从全局的pool中获取buffer。一般的内存管理策略是pool里面的buffer大小全部一致(比如1k),但是 如果需要申请2k的空间,必须要新建2k空间的buffer。如果频繁申请大于1K空间内存,则性能比较低下。 netty为了解决该问题,使用了较为复杂的内存管理策略,具体可参考 http://blog.csdn.net/youaremoon/article/details/47910971 
3:零拷贝:前面提到拷贝数据的性能问题,采用零拷贝机制可有效解决该问题
CompositeByteBuf(组合): 比如读取request Line,申请1k的空间ByteBuf,如果没有发现边界(CRLF)。再申请1k的空间ByteBuf到JDK的io中读取数据。将老的ByteBuf和新申请的ByteBuf组合成CompositeByteBuf,更改CompositeByteBuf的读写指针来避免数据的拷贝。
slice(切分):  比如在1k的ByteBuf里面先读取requestLine,Header进行解析对象,最后读取body。由于body的数据还需要保存在内存里面供业务使用。一般的做法是新申请一块空间,将body的数据拷贝到新申请的空间上。这里通过虚拟一个ByteBuf,然后将读写的指针指向真实的ByteBuf的body区域上面,来避免数据的拷贝。

压缩实现,在HttpResponseEncoder之前加上 HttpContentCompressor 。response对象先进过HttpContentCompressor 压缩后,再经过HttpResponseEncoder进行序列化。
1:压缩主要是针对body进行压缩。http1.1不支持对header的压缩。
2:压缩后body的输出是trunked,而不是Content-length的形式。
Gzip格式,gzip压缩后主要包含三部分:
gzip头:主要存储的是gzip的压缩方式
deflate编码:内容采用的是deflate压缩算法
gzip尾:主要是采用CRC32算法对编码内容进行校验。

安全配置

参数推荐返回错误码描述
requst Line size2k414主要是限制url的长度
header size4k414避免header过长
body size60M413此处一般和业务关联,一般设置相对较大
keepalive timeout75 如果连接在设定时间内没有使用,则关闭掉连接,避免维护的连接过多

http协议解析

http协议主要使用CRLF进行分割。

标示ASCII描述字符
CR 13Carriage return (回车) \n
LF 10Line feed character(换行)\r
SP 32Horizontal space(空格) 
COLON 58COLON(冒号):

请求包,主要包含三部分:请求行(line),请求头(header),请求正文(body) 

请求行(Line):主要包含三部分:Method ,URI ,协议/版本。 各部分之间使用空格(SP)分割。整个请求头使用CRLF分割。(比如:POST /1.0.0/_health_check HTTP/1.1 CRLF)

请求头(Header): 格式为(name :value),用于客户端请求的描述信息。header之间以CRLF进行分割。最后一个header会多加一个CRLF。( 比如:Connection: keep-alive CRLF CRLF)

请求正文(body) :里面主要是Post提交的数据(可支持多种格式,格式在Content-Type定义,长度是在Content-Length里面定义)。

响应包,主要包含三部分:状态行(line),响应头(header),响应正文(body)

状态行(line):包含三部分:http版本,服务器返回状态码,描述信息。以CRLF进行分割。 ( 比如:HTTP/1.1 200 OK CRLF)

响应头(header) : 格式为(name :value),用于服务器返回的描述信息。header之间以CRLF进行分割。最后一个header会多加一个CRLF (比如:Content-Type: text/html CRLF Content-Encoding:gzip CRLF CRLF) 

响应正文(body):里面主要是返回数据(可支持多种格式,格式在Content-Type定义,长度是在Content-Length里面定义)。

truncked协议

1:主要包含三部分:chunk,last-chunk和trailer。如果分多次发送,则chunk有多份。
2:chunk主要包含大小和数据,大小表示这个这个trunck包的大小,使用16进制标示。其中trunk之间的分隔符为CRLF。
3:通过last-chunk来标识chunk发送完成。 一般读取到last-chunk(内容为0)的时候,代表chunk发送完成。
4:trailer 表示增加header等额外信息,一般情况下header是空。通过CRLF来标识整个chunked数据发送完成。

HTTP协议通常使用Content-Length来标识body的长度,在服务器端,需要先申请对应长度的buffer,然后再赋值。接收数据时,发现header中有Content-Length属性,则读取Content-Length 的值,确定需要读取body的长度。

如果需要一边生产数据一边发送数据,就需要使用”Transfer-Encoding: chunked” 来代替Content-Length,也就是对数据进行分块传输。按照truncked协议分批读取数据。

压缩类型

1:压缩需要客户端,服务器端同时支持。在chrome中,请求默认会加上Accept-Encoding: gzip, deflate,客户端默认开启数据压缩。而tomcat默认关闭压缩,如果开启需要增加配置。
2:在请求时,需要通过header的Accept-Encoding: gzip, deflate 来告诉服务器客户端支持的压缩类型。
3:在返回时,http server会在返回的header中添加Content-Encoding: gzip 来告诉客户端数据的压缩方式。
4:压缩类型主要包含如下几种:
         gzip      说明body采用GNU zip编码
         compress  说明body采用Unix的文件压缩程序
         deflate  说明body是用zlib的格式压缩的
         identity  说明没有对实体进行编码。
其中 gzip, compress, 以及deflate编码都是无损压缩算法,不会导致信息损失。 gzip效率最高,使用较为广泛。

http解决粘包拆包

1:请求行的边界是CRLF,如果读取到CRLF,则意味着请求行的信息已经读取完成。
2:Header的边界是CRLF,如果连续读取两个CRLF,则意味着header的信息读取完成。
3:body的长度是有Content-Length 来进行确定。如果没有Content-Length ,则是chunked协议(具体参考前面的trunked协议)。

Android AndroidX的迁移

Google 2018 IO 大会推出了 Android新的扩展库 AndroidX,用于替换原来的 Android扩展库,将原来的android.*替换成androidx.*;只有包名和Maven工件名受到影响,原来的类名,方法名和字段名不会更改。接下来我们来看看使用 AndroidX的扩展库需要哪些配置。

1. AndroidX变化

1)常用依赖库对比:

Old build artifactAndroidX build artifact
com.android.support:appcompat-v7:28.0.2androidx.appcompat:appcompat:1.0.0
com.android.support:design:28.0.2com.google.android.material:material:1.0.0
com.android.support:support-v4:28.0.2androidx.legacy:legacy-support-v4:1.0.0
com.android.support:recyclerview-v7:28.0.2androidx.recyclerview:recyclerview:1.0.0
com.android.support.constraint:constraint-layout:1.1.2androidx.constraintlayout:constraintlayout:1.1.2

 更多详细变化内容,可以下载CSV格式映射文件;

2)常用支持库类对比:

Support Library classAndroidX class
android.support.v4.app.Fragmentandroidx.fragment.app.Fragment
android.support.v4.app.FragmentActivityandroidx.fragment.app.FragmentActivity
android.support.v7.app.AppCompatActivityandroidx.appcompat.app.AppCompatActivity
android.support.v7.app.ActionBarandroidx.appcompat.app.ActionBar
android.support.v7.widget.RecyclerViewandroidx.recyclerview.widget.RecyclerView

 更多详细变化内容,可以下载CSV格式映射文件。

2. AndroidX配置

1)更新升级插件

  • 将AS更新至 AS 3.2及以上;
  • Gradle 插件版本改为 4.6及以上;
    项目下 gradle/wrapper/gradle-wrapper.propertie 文件中的distributionUrl改为:
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
  • compileSdkVersion 版本升级到 28及以上;
  • buildToolsVersion 版本改为 28.0.2及以上。

插件更新提示

2)开启迁移AndroidX
 在项目的gradle.properties文件里添加如下配置:

android.useAndroidX=true
android.enableJetifier=true

 表示项目启用 AndroidX 并迁移到 AndroidX。

3)替换依赖库
 修改项目app目录下的build.gradle依赖库:

implementation 'com.android.support:appcompat-v7:28.0.2' → implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.android.support:design:28.0.2'  → implementation 'com.google.android.material:material:1.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2' → implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
...

4)修改支持库类
 将原来importandroid.**包删除,重新import新的androidx.**包;

import android.support.v7.app.AppCompatActivity; → import androidx.appcompat.app.AppCompatActivity;

5)一键迁移AndroidX库
 AS 3.2 及以上版本提供了更加方便快捷的方法一键迁移到 AndroidX。选择菜单上的ReFactor —— Migrate to AndroidX… 即可。(如果迁移失败,就需要重复上面1,2,3,4步手动去修改迁移)

AndroidX 迁移

注意:如果你的项目compileSdkVersion 低于28,点击Refactor to AndroidX…会提示:

You need to have at least have compileSdk 28 set in your module build.gradle to refactor to androidx

提示让你使用不低于28的sdk,升级最新到SDK,然后点击 Migrate to AndroidX…,AS就会自动将项目重构并使用AndroidX库。

3. AndroidX迁移问题

《Android Support库和AndroidX冲突问题》

4. AndroidX影响

  虽然说目前对我们没有多大影响,我们可以不使用仍然使用旧版本的支持库,毕竟没有强制,但长远来看还是有好处的。AndroidX重新设计了包结构,旨在鼓励库的小型化,支持库和架构组件包的名字也都简化了;而且也是减轻Android生态系统碎片化的有效方式。

参考

AndroidX 官方文档,开启传送门

三生有幸遇见你纵使悲凉也是情

三生有幸遇见你纵使悲凉也是情

三生有幸遇见你纵使悲凉也是情是什么意思:

1、不论结局如何,很高兴能认识你

2、很庆幸能够与你相遇,与你相识,与你相知,并与你相爱。今生有你,不论在一起会遇到什么样的困难,甚至离别,都不会后悔,情之所至。

三生有幸遇见你下一句是什么:

1、三生有幸遇见你,纵然悲凉也是情,知卿心系在我心,不枉今生爱一场!

2、大千世界,处处繁花似锦,能够遇见你三生有幸。

3、三生有幸遇见你,人生只有两次幸运就好,一次遇见你,一次走到底。

4、醒时捕光,睡时捉梦 ,不如遇见你三生有幸。

5、城南小陌又逢春,只见梅花不见人。

人有生老三千疾,唯有相思不可医

本是青灯不归客,却因浊酒恋红尘。

三里清风三里路,步步风雨再无你。

终是庄周梦了蝶,你是恩赐也是劫。

三生有幸遇见你,纵使悲凉也是情。

6、三生有幸遇见你,十里桃花马蹄急; 只闻锦瑟风乍起,但将心事付瑶琴。

7、道生一,一生二,二生三,三生有幸遇见你。太极生两仪,两仪生四象,四象生八卦,八卦衍万象,万象不如你。

8、三生有幸遇见你,一直相随永不弃。

9、三生有幸遇见你,无关风月也是情。

10、听一曲风声,画两道身影,说三生有幸遇见你。

Centos7 切换 GCC 版本

查看gcc版本是否在5.3以上,centos7.6默认安装4.8.5

gcc -v

升级gcc到5.3及以上,如下:升级到gcc 9.3

yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
scl enable devtoolset-9 bash

需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。
如果要长期使用gcc 9.3的话:
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile
这样退出shell重新打开就是新版的gcc了
以下其他版本同理,修改devtoolset版本号即可。

胡不归问题

胡不归问题,是一个非常古老的数学问题,曾经是历史上非常著名的“难题”。近年来陆续成为各地中考模拟题的小热门考点,学生不易把握,今天给大家普及讲解一下。

话说,从前有一小伙子外出务工,某天不幸得知老父亲病危的消息,便立即赶路回家.小伙子略懂数学常识,考虑到“两点之间线段最短”的知识,就走布满沙石的路直线路径,而忽视了走折线虽然路程多但速度快的实际情况,当赶到家时,老人刚咽了气,小伙子追悔莫及失声痛哭.邻居告诉小伙子说,老人弥留之际不断念叨着“胡不归?胡不归?…”

这个问题引起了人们的思索,小伙子能否节省路上时间提前到家?如果可以,他应该选择一条怎样的路线呢?这就是流传千百年的“胡不归问题.

如图,A是出发点,B是目的地,直线AC是一条驿道,而驿道靠目的地一侧全是砂土,为了选择合适的路线,根据不同路面速度不同(驿道速度为a米/秒,砂土速度为b米/秒),小伙子需要在AC上选取一点D,再折往至B.

上述数学解释用到了三角函数知识将两个线段的系数权重都化为1,从而降低了求最值难度。聪明的同学或许一下就发现转化成了我之前讲过的“将军饮马(小河取水)”模型,进而作对称求得最值。

spring cloud config server

配置使用 git 时候 http 方式比较容易,证书有问题时加上skip-ssl-validation: true ssh 方式,配置尽量使用本地配置方式,配置文件要简单,只配置 uri 使用 git 协议就行
不要配置其他参数,特别是username,我配置时候踩坑了

世道与人心

两个不同故事,同样的选择,简明解释世道与人心

故事1:一个富二代看下一个穷姑娘,假装穷小子接近,后来成为情侣, 有一天男子家境暴漏
故事2:一个穷小子看下一个穷姑娘,假装富二代接近,后来成为情侣, 有一天男子家境暴漏

姑娘的选择:A)我不在乎钱,只想和他一起 B)渣男,欺骗我感情,分手

故事1,多数选 A 这就是世道
故事2,多数选 B 这就是人心

JDK 14 正式发布,新特性关注

2020年3月17日JDK 14 正式发布了,其中还是有一些值得关注的新特性。如果你觉得我写的东西对于您有帮助,希望得到您的关注!

一、instanceof的模式匹配:

Instanceof是java中用于检查对象引用是否为给定Type类型的实例,并返回布尔值。在Java 14之前,我们在完成判断之后,经常需要做一下类型的强制转换,如下:

if (obj instanceof String) {
    String str = (String) obj; // 需要强转
    .. str.contains(..) ..
}else{
     ....
}

Java 14增强功能特性:

if (!(obj instanceof String str)) {
    .. str.contains(..) .. // 不再需要转换代码,实际发生了转换
} else {
    //.. str.... 这里 str 会是未定义
}

更多示例:

if (obj instanceof String str && str.length() > 5) {.. str.contains(..) ..}

if (obj instanceof String str || str.length() > 5) {.. str.contains(..) ..}

注意:仅当object不为null时,instanceOf才会匹配,然后仅将其分配给str。在instanceof中使用模式匹配可以减少Java程序中显式强制转换的数量。

二、NullPointerExceptions(注意带s)

在java 14之前,我们经常在调试代码的时候,发现一行代码中有多个对象,抛出异常之后你也无法确定到底是哪个对象为null。假设此代码中出现一个NullPointerException:

 a.b.c.i = 0;

//下面是异常信息
Exception in thread "main" java.lang.NullPointerException
                            at Prog.main(Prog.java:5)

文件名和行号不能精确指出哪个变量为空。是a还是b或c?JDK14对此做了改进。

Exception in thread "main" java.lang.NullPointerException:
        Cannot read field 'c' because 'a.b' is null.
    at Prog.main(Prog.java:5)

但是,这也存在一些风险。空指针异常消息包含源代码中的变量名。暴露此信息可能被视为程序的安全风险。

三、switch表达式

在Java 14之前*

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}

Java 14增强功能

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

四、record

Java 14使用record关键字来减少类声明语法,这有点像lombok。我们有时候需要编写许多低价值的重复代码来实现一个简单的数据载体类:构造函数,访问器,equals(),hashCode(),toString()等。为了避免这种重复代码,Java 14推出record。 java14之前的代码:

final class Point {
    public final int x;
    public final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // 很多的equals, hashCode, toString,getters、setters
}

Java 14中就用这一行代码

record Point(int x, int y) { }

五、文本块

在Java中,要将HTML,XML,SQL或JSON代码片段嵌入到代码中通常很难阅读,为了克服此问题,Java 14引入了Text Block。 java14之前,没有文本块的HTML示例

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";

java14,带文本块的HTML示例,下面的代码看上去是多行的,实际上字符串的拼接结果仍然是一行的。

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

如果你希望字符串中有换行,在每行的行尾加上“\”,这样字符串就是换行的了,可以打印出来看一下。

String html = """
              <html>  \
                  <body>  \
                      <p>Hello, world</p>  \
                  </body>  \
              </html>
              """;

IDEA2019.3安装和激活

之后进到这个界面,点击 Configure -> Edit Custom VM Options

在最后加上   -javaagent:/data/jetbrains-agent.jar

 这个jar包是需要用来破解的jar包:   https://files.cnblogs.com/files/quyf/jetbrains-agent.zip

IntelliJ IDEA 破解之后,用了一段时间后,打开软件提示 no suitable licenses left on the license server

需要让我们重新注册,原来是之前的地址服务更改为了新的地址: http://fls.jetbrains-agent.com

所以我们在使用服务器激活的时候使用新的地址代替原来的地址即可:http://jetbrains-license-server/

沟通难四个原因

《PMBOK 指南》将沟通管理划为一个专门的知识领域,建议项目经理花 75% 以上的时间在沟通上。根据美国普林斯顿大学的调查报告,在所有对工作产生影响的因素中,沟通占的比例高达 75%。而我们工作中出现的 80% 问题都是由沟通不当造成的,可见沟通的重要性,因为沟通实在太难了。多数时候,我们只想着表达自己的观点,只关注自己想说什么,我们会尽量使用漂亮的 PPT、华美的语言、一堆的数据、甚至引章据典,而不关心别人听懂没有,没有思考别人是否想听,别人是否听得懂。

沟通难主要出于以下四个原因:

第一种是由于立场、利益、背景的原因,当双方缺乏信任,或处于敌对状态时是无法沟通的,这个时候的沟通和所说内容的对错没有必然的关系,对方只想找差错,找到了就会理解为阴谋论,并且非常兴奋,所谓道不同不相为谋。这是最困难的沟通场景,越沟通,矛盾分歧会越大,实际是无效沟通。在公司中,经常会因为组织架构设计的不合理,造成团队利益的冲突,从而产生很多的无效沟通。

第二种是由于语言、专业知识、职位、环境的巨大差异,造成沟通方面的巨大鸿沟,本质上是思维方式、常识、知识储备的不一致造成的认知差异,这样的沟通成本非常巨大,需要恶补相关知识,参加专业培训消除鸿沟才能够创造沟通的条件。

第三种是由于沟通信息衰减造成的,语言文字是我们主要的沟通方式,但是很多时候光靠语言文字会有歧义,比如我们对名词概念的理解可能会有不同,甚至可能会完全相反,对语言中所带情绪的理解也不同,而这些都会造成信息的压缩。有研究表明,对话沟通中语言起到的作用仅占 38%,而肢体语言所占的比例高达 55%。你想表达的意思和你说出来的话语会有差异,对方听到的信息和对方理解的意思又会有差异,这就形成了我们通常说的沟通漏斗:
第一个漏掉 20%:你想表达的是 100%,实际表达的只有 80%,主要原因有语言词汇的限制等;
第二个漏掉 20%:听者只接收了 60%,主要原因有信息衰减、听得不仔细等;
第三个漏掉 20%:听者只理解、听懂了 40%,主要原因有词语理解能力、注意力不集中等;
第四个漏掉 20%:最后听者只记住了 20%,主要原因是没有反馈、容易忘记等;而随着时间的推移,如果不持续做交流沟通,最后的 20% 也会被忘记。

第四种沟通障碍是沟通交流者的心态,这个又和企业、团队的组织架构及文化有关,以下举例一下可能存在的心态对沟通的影响:刚刚进入

充满信任的技术团队

LinkedIn 首席执行官杰夫·韦纳(Jeff Weiner)认为,“在时间的流逝中保持一致就是信任”,这个“一致”有很多含义,如目标的一致、行动的一致等;微软首席执行官萨提亚·纳德拉(Satya Nadella)认为,“信任 = 同理心 + 共同价值观 + 安全可靠”,他把“同理心”放在了信任等式的第一位,认为无论做什么事情,都需要大家对所做的事情产生共鸣。

团队合作中,每个成员的工作多多少少都会和其他人有一些上下游交互,如果上游的人总是能够对下游人的诉求快速响应,无疑会让下游的人感觉更加安心。以我们的交付团队为例,我们有专职的集成测试团队,他们要负责软件发布前的最后一轮验收,但是开发团队的交付延迟总是会把集成测试团队搞得手忙脚乱,团队内的相互指责也从来没有停止过。后来,我们引入了统一研发看板系统,使得每一个员工的任务都在看板上可见,任何下游的同事都可以看到其依赖的上游员工的进展和潜在的风险。通过这套实时反馈系统,下游员工可以提前了解风险,以便及时采取应对措施,那种一无所知的不信任感很容易就消除了。可见,员工之间要及时进行沟通,才能及时获取自己关切的信息,团队人越多,沟通效率越低,越要想办法增加沟通的带宽,而构建可以透明呈现所有人信息的系统是一个不错的实践。

1994 年,心理学家 Freeston 等人提出了“无法容忍不确定的程度(The Intolerance of Uncertainty)”这一概念,简称 IU。一系列研究认为,IU 是担心、焦虑产生和维持的关键影响因素,也是焦虑及焦虑障碍的最重要预测指标。对于不确定的焦虑,会影响我们的知觉控制水平,也就是我们所感知到的“自己能够在多大程度上影响事情的结果”。当我们对不确定的焦虑越高时,我们就会越不相信自己能够影响事情的结果,对自己的贡献就会越不信任。

广告名词 CPA、CPS、CPM、CPT、CPC 、CVR、OCPC、OCPM是什么

1.CPA(Cost Per Action) 每行动成本,这个行为可以是注册、咨询、放入购物车等等。CPA是一种按广告投放实际效果计价方式的广告,即按回应的有效问卷或注册来计费,而不限广告投放量。电子邮件营销(EDM)现在有很多都是CPA的方式在进行。

2.CPS(Cost Per Sales):以实际销售产品数量来换算广告刊登金额。CPS是一种以实际销售产品数量来计算广告费用的广告,这种广告更多的适合购物类、导购类、网址导航类的网站,需要精准的流量才能带来转化。

3.CPC(Cost Per Click) 每点击成本。CPC是一种点击付费广告,根据广告被点击的次数收费。如关键词广告一般采用这种定价模式,比较典型的有Google广告联盟的AdSense for Content和百度联盟的百度竞价广告。

4.CPT(Cost Per Time) 每时间段成本。CPT是一种以时间来计费的广告,国内很多的网站都是按照“一个星期多少钱”这种固定收费模式来收费。这种广告形式很粗糙,无法保障客户的利益。但是CPT的确是一种很省心的广告,能给你的网站、博客带来稳定的收入。阿里妈妈的按周计费广告和门户网站的包月广告都属于这种CPT广告。

5.CPM(Cost Per Mille) 每千人成本。CPM是一种展示付费广告,只要展示了广告主的广告内容,广告主就为此付费。这种广告的效果不是很好,但是却能给有一定流量的网站、博客带来稳定的收入。有人认为,CPM的计算不能按照被看到作为衡量标准,如果一个Banner在页面底部,需要滚屏才能看到,只要这个Banner在该页面中被展示了1000次,即使1000次中没人滚屏到页面底部看这个广告,也应该计算为一个CPM。

fb42c445749f46eab0a100b06b816873_th

(1)CPT和CPM只在第一步收取广告费用,即媒体只需要将广告对广告受众进行了展示,即可向广告商收取广告费用。

(2)CPC只收取第二步费用,消费者看到广告后并进行了点击行为以后,媒体向广告商收取广告费用。

(3)CPA和CPS处于第三步,即消费者有看到广告后并点击了广告,进一步了解活动情况后在广告主的网站完成某些特定行为(例如付款消费,填表注册等)。

f403828429794c43a893db96b1e19925_th

相比而言,CPM和CPT对网站有利,而CPC、CPA、CPS则对广告主有利。目前比较流行的计价方式是CPM和CPC,最为流行的则为CPM。

从广告价格上来分,CPT和CPM的表面价格相对较为低廉,而CPC居中,CPA和CPS的价格则似乎要高很多。需要指出的是,这里说的价格只是表面价格,不等于性价比。一般情况下,CPA和CPS的性价比相对固定,而CPC和CPT、CPM则根据网站对用户的粘性不同而有区别。

从作弊难易程序来看,CPT和CPM的选择,通常取决于网站的质素,而网站的质素,衡量标准似乎只有网站统计和第三方统计,但这些都是很容易作弊的,国内满天飞的刷流量软件和网站,就是CPT和CPM作弊的最佳工具,尽管站长们都知道,但是广告商却只能在一段时间内广告效果不如意时才能有所察觉。

对于CPC广告,尽管存在一定的技术防范措施,作弊也相对容易,国内甚至有网站出售点击广告包月,这也是导致国内CPC广告联盟式微的原因吧。而CPA也相对较为容易,只要有足够多的时间和精力去注册和验证虚假用户。唯CPS广告几乎不在乎作弊,也是几个广告类型中唯一很难作弊的广告模式。

7dd98d1001e93901abaa5c1e75ec54e736d19662

1.CVR (Click Value Rate): 转化率,衡量CPA广告效果的指标

2.CTR (Click Through Rate): 点击率

3.ROI: Return On Investment,投资回报率,或者说 投资利润率。指通过投资而应返回的价值,它涵盖了企业的获利目标。利润和投入的经营所必备的财产相关,因为管理人员必须通过投资和现有财产获得利润。又称会计收益率、投资利润率。