特性: Java7: 1.switch可以使用字符串 2.泛型实例化的时候自动判断类型 3.语法上支持集合,不一定是数组,final List<Integer> piDigits = [ 1,2,3,4,5,8 ]; 4.map集合支持并发请求,且可以写成 Map map = {name:"xxx",age:18}; Java8: 1.允许接口添加默认方法 2.lambda表达式 3.函数式接口 4.支持传递方法和构造函数的引用,使用::关键字 5.lambda表达式中可以访问外部的局部变量 6.optional接口,用来判断是否为空 7.filter,map,sort,match等流操作
网络架构: LVS:Linux虚拟机,流量调度,防止机器过载 CDN:内容分发,加快用户访问速度 proxy:代理网关,内外网数据交互 nginx:高性能代理服务器,系统内部的流量分发
稳定性套路:1.容量评估(流量梳理) provider 知道多少业务方掉用,接口级别 ,分别知道有多少qps(峰值) consumer 调用了哪些服务,对它有多少的压力 比如:cache,db,map,solr 2.监控(报警) 3.单机压测(引流,模拟请求) 4.开关,限流,预案 5.全链路压测 6.加机器,优化,循环
限流不是万能的限流处理不了强依赖,例如系统依赖的cache挂了,线程拖慢了也无法处理主要是慢请求的问题,最容易遇到,所有线程占满了之后,也没办法响应其他请求了
流量大规模超过负荷,限流可以解决接口没有设限,数据量过大,可以并发限流,限制响应的线程数
消息系统:push方式broker节点主动向consumer实时发送数据优点:实时性好,可以第一时间发送消息缺点:当broker节点流量很大时,consumer端可能扛不住,consumer端流量不可控
pull方式consumer端变被动为主动,定时主动轮询向broker端拉取数据优点:consumer端自身的压力和它带给broker的压力都可控缺点:实时性不好,无法第一时间获取到数据
1.p2p,即集群消费,多个consumer组成一个consumer的集群,对topic进行消费,一条消息处理一次,要求对于消息的处理是幂等的2.pub-sub,广播,由broker向所有consumer广播消息,保证每一个consumer都会收到消息
中间件:
- 消息系统 metaQ,notify,ONS,rocketMQ,kafka
- 服务框架 HSF,和服务框架配套config server tesla(fusion)
- 数据分发层:TDDL(raptor)
- 数据迁移:DataX,愚公,精卫(数据交换)
- 软配置:Diamond
- 容器:AliTomcat、JStorm、Flink
- 业务性的:定时任务、DCP(对账)、日志
网络编程中常用术语:同步、异步、阻塞、非阻塞select,poll,epoll,pselect 参考:http://www.cnblogs.com/Anker/p/3265058.html 同步:在发出一个调用时,等所有所有操作执行完再返回结果异步:调用成功后直接返回,等执行完毕后再通过回调或者状态等方式通知阻塞:调用是指调用结果返回之前,当前线程会被挂起等待。调用线程只有在得到结果之后才会返回非阻塞:指在不能立刻得到结果之前,该调用不会阻塞当前线程,而是会不断轮询,一直到有结果为止总体来说:同步异步是基于内核说的,阻塞非阻塞针对IO说的
select:几乎支持所有平台,然而能监视的文件描述符有数量限制,最多为1024个,而且对于socket采用线性扫描,效率较低
poll:和select类似,也采用轮询的方式检查设备状态,不过它是采用链表实现的,所以相比之下没有容量大小的限制,效率一样低
epoll:更加灵活,使用一个文件描述符管理多个描述身上,将文件描述符的事件存放进时间表中,这样的话用户空间和内核空间只需要copy一次。优点主要有:没有并发链接的限制,效率提升,不是采用轮询的方式,不会随着FD数目的增加而降低,内存拷贝方面做了优化,使用mmap减少复制开销
pselect: 和select()类似,它增加了超时值的精度,并且可以在等待文件描述符“准备好”的同时指定一组信号掩码。
NIO
在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(,而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
NIO怎么解决这个问题呢,采用reactor模式
当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。 也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的
RPC:consumer:服务消费方,类似Tesla中的服务调用方producer:服务提供方register:注册,类比于Tesla中的console注册平台。往往由多台机器,分布式环境分布式环境中往往经常会遇到某一台机器突然挂了不可使用的情况,这时候不能把原本的流量全部打向其他机器,可能会导致其他机器流量瞬间过大,会有系统级别的稳定性隐患
三者之间全是长连接:类似于数据库连接池和线程池的做法,一方面吗是因为维持心跳短链接开销会很大,还有链接效率考虑
如果服务挂了,该怎么办:1.sleep,重试2.failback,打印日志3.failover,ip列表轮询,如果查询一台失败了之后,会找集群的另一台机器重试4.failfast,抛异常,failsafe什么都不做5.failbroadcast广播到所有集群6.fork并行调用,由于可能会重复调用,需要注意幂等性的问题,不是幂等性的操作会有并发问题
RPC负载均衡的方式1.时间响应,利用优先队列实现,以最后一次响应时间为优先因子,即是最后一次响应越早的优先级越高2.轮询3.随机4.一致性hash
要保证线程安全,需要保证原子性和一致性
原子性指的是不可分割的多个操作,比如转账操作中扣钱和汇款操作。要么一起执行,要么都不执行。
一致性一般指的是数据的一致性,即多个线程进行读取或者是数据的操作,得到的结果应该保证一致
多线程:
volatile 保证的是可见性,但不能保证原子性。也就是说可以保证多个线程访问一个变量时不会存在因为线程内缓存导致的值不更新的问题,但是保证不了原子性即当多个线程对变量进行操作的时候,保证不了原子性,这时候需要通过synchronized来实现指令优化,在32位的机器中,64位的long会被拆成两个32位进行处理,可能会导致数据问题,如果加上volatile关键字,则不会进行这样的拆分,可以理解成指令优化
锁削除:即对不会产生线程隐患的锁进行削除
锁膨胀:当虚拟机发现一个连续操作都是对同一个对象反复加锁和解锁,那么虚拟机则会将这个锁膨胀到整个操作序列的外部。可以视为整个连续加解锁的代码被整体加上了一个区域锁
轻量级锁:也即是建立无锁CAS(compare and swap)的hash表来实现模拟加锁的一种算法。如果出现两个线程抢占同一个锁的情况,轻量级锁会膨胀成重量级锁
偏向锁:JVM会偏向第一个获得该锁的线程,在接下来的操作中,如果该锁没有被其他线程获得,那么持有该锁的线程进行操作时都不需要进行同步。偏向锁是一个带有效益权衡(Trade Off)性质的优化,也就是说它并不一定总是对程序运行有利。如果该资源总是会被不同的线程持有,那么偏向锁优化就是多余的
多线程实现方式:
继承thread实现实现runnable接口因为extends了类之后就不能再继承thread类,这个时候可以实现runnable接口来实现多线程实现callable接口通过FutureTask来创建thread线程线程池
run和start方法有何区别start是另起线程,而run只是普通的方法调用,并不会新建新的线程start方法会自动在新建出的线程里调用run方法
线程状态:
NEW:状态是指线程刚创建, 尚未启动RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)JVM内存模型:
虚拟机栈:存放基本数据类型,对象引用(可能是指针或者是句柄)方法栈:存放Native的方法服务堆:存放对象的实例(Eden、s0、s1,老年代)perm区:静态变量、常量、类信息堆外内存:只能通过Full GC回收
young GC:复制算法,保证了空见的连续性也避免了空间的大量浪费CMS GC:针对老年代
spring: ioc:控制反转,将对实例的依赖交给容器控制。业务逻辑的流程由对象关系图决定,有三种注入方式:set方法注入、接口注入、构造器注入 aop:面向切面编程,针对切入点进行通知,比如before、after接口
spring作用域:
- singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
- prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
- request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
网友评论