目录
1.集合
2.消息队列
3.非关系型数据库
4.线程
5.nignx
6.io流
7.对象
8.spring cloud
9.spring中间件
集合分为:collection和Map:
如何决定是使用hashmap还是treemap?
如果你需要得到一个有序的结果时就应该使用TreeMap(因为HashMap中元素的排列顺序是不固定的)。除此之外,由于HashMap有更好的性能,所以大多不需要排序的时候我们会使用HashMap。
常见的list集合
①:ArrayList
特点是:数组结构、查询快、增删慢、运行效率快,线程不安全
三个实现接口(Serializable、Cloneable、RandomAccess)
扩容方面的问题:第一次扩容是10、以后每一次的扩容都是原容量的1.5倍
改进扩容造成的性能低下问题:在可预知的范围内指定构造方法中传入指定的大小
ArrayList在使用迭代器遍历时,进行集合的增删操作则会抛出并发修改异常,如何避免这一问题?
①:通过迭代器迭代元素,迭代器修改元素 、而Iterator迭代器却没有添加功能,所以我们使用其子接口ListIterator
②:遍历集合的元素、集合修改元素(普通for循环)
②:vector集合
数组结构,查询快,增删慢,运行效率慢,线程是安全的
增删的方法被synchronized 关键字修饰,因此为线程相对安全安全
③:linkedList集合
双向链表结构,增删块,查询慢,线程是不安全的
消息是指在应用期间传送的数据,消息可以非常简单,比如只包含文本字符串,也可以复杂化,比如包含嵌入对象。消息队列是一种应用间的通信方式,消息发送后可以立即返回,有消息系统来确保信息的可靠传递。
特点是:可靠性、灵活性、高可用、多种协议、多语言等
其好处主要是:提高系统的响应速度、保证消息的传递、解耦。
例如一个业务系统需要发送短信,但是短信模块速度跟不上,业务系统就可以把发送短信的相关信息封装为一个消息,放入队列,短信发送消息从队列中获取消息进行处理。
①:mongo
是一个基于分布式文件存储的数据库,由c语言编写,是一个介于关系型数据库与非关系型数据库之之间的产品,最大的特点是它支持的查询语言很多。
相对于mysql而言,mongo在需要使用大量的地理位置查询、文本查询;要求存储的数据不丢失等应用场景,会比mysql以成本更低的形式解决问题。
②:redis
redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。
redis是一种高级的key:value存储系统,其中value支持五种数据类型:
1.字符串(strings)
strings类型是一个很基础的数据类型,也是任何存储系统都必备的数据类型。字符串类型的用法就是这么简单,因为是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。
2.字符串列表(lists)
redis中的lists在底层实现上并不是数组,而是链表,也就是说对于一个具有上百万个元素的lists来说,在头部和尾部插入一个新元素,其时间复杂度是常数级别的,比如用LPUSH在10个元素的lists头部插入新元素,和在上千万元素的lists头部插入新元素的速度应该是相同的。
虽然lists有这样的优势,但同样有其弊端,那就是,链表型lists的元素定位会比较慢,而数组型lists的元素定位就会快得多。
3.字符串集合(sets)
redis的集合,是一种无序的集合,集合中的元素没有先后顺序。对于集合的使用,也有一些常见的方式,比如,QQ有一个社交功能叫做“好友标签”,大家可以给你的好友贴标签,比如“大美女”、“土豪”、“欧巴”等等,这时就可以使用redis的集合来实现,把每一个用户的标签都存储在一个集合之中。
4.有序字符串集合(sorted sets)
有序集合中的每个元素都关联一个序号(score),这便是排序的依据。
很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的,比如zrange、zadd、zrevrange、zrangebyscore等等
5.哈希(hashes)
哈希是从redis-2.0.0版本之后才有的数据结构。
hashes存的是字符串和字符串值之间的映射,比如一个用户要存储其全名、姓氏、年龄等等,就很适合使用哈希。
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口
①:线程状态转换
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
②:线程调度
线程的调度
1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。(1~10)
2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。当睡眠结束后,就转为就绪(Runnable)状态。
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法行为等价于调用 wait(0) 一样。
4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个wait 方法,在对象的监视器上等待。直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。
③:常见的线程名词解释
主线程:JVM调用程序main()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。 用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
线程类的一些常用方法:
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
④:线程同步
synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。
1、线程同步的目的是为了保护多个线程访问一个资源时,对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使。但是,一旦程序发生死锁,程序将死掉。
⑤:线程数据传递
1.通过构造方法传递数据
在创建线程时,必须要建立一个Thread类的或其子类的实例。因此,我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来,以便线程使用(其实就是在run方法中使用)。
这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了,这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据,可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。
2.通过变量和方法传递数据
向对象中传入数据一般有两次机会,第一次机会是在建立对象时通过构造方法将数据传入,另外一次机会就是在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。
通过回调函数传递数据
3.通过回调函数传递数据
上面的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说,是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据。这时候就需要用到回调函数。
①:概述
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器
其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好
处理高并发能力是十分强大的,能经受高负 载的考验
支持热部署,启动简单
②:代理
正向代理: 我们平时需要访问国外的浏览器是不是很慢,比如我们要看推特,看GitHub等等。我们直接用国内的服务器无法访问国外的服务器,或者是访问很慢。所以我们需要在本地搭建一个服务器来帮助我们去访问。那这种就是正向代理。(浏览器中配置代理服务器)
反向代理: 那什么是反向代理呢。比如:我们访问淘宝的时候,淘宝内部肯定不是只有一台服务器,它的内部有很多台服务器,那我们进行访问的时候,因为服务器中间session不共享,那我们就需要在服务器之间频繁登录,那这个时候淘宝搭建一个过渡服务器,对我们是没有任何影响的,我们是登录一次,但是访问所有,这种情况就是 反向代理。对我们来说,客户端对代理是无感知的,客户端不需要任何配置就可以访问,我们只需要把请求发送给反向代理服务器,由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器地址,隐藏了真实服务器的地址。(在服务器中配置代理服务器)
③:负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。
负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
Nginx给出来三种关于负载均衡的方式:
轮询法(默认方法):
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
适合服务器配置相当,无状态且短平快的服务使用。也适用于图片服务器集群和纯静态页面服务器集群。
weight权重模式(加权轮询):
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
这种方式比较灵活,当后端服务器性能存在差异的时候,通过配置权重,可以让服务器的性能得到充分发挥,有效利用资源。weight和访问比率成正比,用于后端服务器性能不均的情况。权重越高,在被访问的概率越大
ip_hash:
上述方式存在一个问题就是说,在负载均衡系统中,假如用户在某台服务器上登录了,那么该用户第二次请求的时候,因为我们是负载均衡系统,每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的。
我们可以采用ip_hash指令解决这个问题,如果客户已经访问了某个服务器,当用户再次访问时,会将该请求通过哈希算法,自动定位到该服务器。每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
④:动静分离
Nginx的静态处理能力很强,但是动态处理能力不足,因此,在企业中常用动静分离技术。动静分离技术其实是采用代理的方式,在server{}段中加入带正则匹配的location来指定匹配项针对PHP的动静分离:静态页面交给Nginx处理,动态页面交给PHP-FPM模块或Apache处理。在Nginx的配置中,是通过location配置段配合正则匹配实现静态与动态页面的不同处理方式
⑤:常用命令
cd /usr/local/nginx/sbin/
./nginx 启动
./nginx -s stop 停止
./nginx -s quit 安全退出
./nginx -s reload 重新加载配置文件 如果我们修改了配置文件,就需要重新加载。
ps aux|grep nginx 查看nginx进程
①:概念
对于数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input 和输出output ,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
②:流的分类
按照方向分:输入流(InputStream)、输出流(OutputStream)
按照数据类型分:字节流(InputStream、OutputStream)、字符流(Reader、Writer)
③:作用以及应用场景
文件复制、文件上传、文件下载
如果操作的是纯文本文件,优先使用字符流
如果操作的是二进制文件,优先使用字节流
如果不确定,则优先使用字节流
④:字节流
字节输入流:InputStream
读数据,read一次可以读取一个字节。read(bytes [])一次读取一个字节数组
细分为:FileInput Stream、BufferedInput Stream
字节输出流:OutputStream
写数据:write一次写一个字节。write(bytes [])一次写一个字节数组
细分为:FileOutputStream、BufferedOutputStream
字节流写数据:
基本步骤:①:创建File对象,关联到一个文件路径;②:调用write方法,写出数据;③:调用close方法,释放资源;④:打开文件,查看内容。
⑤:字节缓冲流
BufferedOutputStream
类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
BufferedInputStream
创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
⑥:字符流
概念:字符流 = 字节流 + 编码表
字符输入流:reader
字符输出流:writer
⑦:字符缓冲流
BufferedWriter
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途。
BufferedReader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。
⑧:特殊操作流
1.标准输入输出流:该流对应于键盘输入或由主机环境或用户指定的另一个输入源
2.打印流:分为字节打印流(PrintStream)、字符打印流(PrintWriter)。
3.对象序列化流:将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
4.对象反序列化流:反序列化先前使用ObjectOutputStream编写的原始数据和对象。
①:概念:
1.对象是由我们自己定义的类来创建出来的。
2.对象实际上就是类的具体实现。
例如:修建大楼时绘制的设计图纸就是类,根据设计图纸修建起来的真实的可以住人的
大楼就是对象。
类--抽象【抽取象什么一样的东西】--模板【设计图】
对象--实现--实例【楼房】
3.没有类就没有对象
4.一个类可以创建出多个对象
5.类是对象的模板,对象是类的真实表现
对象的作用
调用类中的变量和方法
②:创建对象:
1.当前类中--new+构造方法---this
2.其他类中--new+构造方法
格式 : new 构造方法( [参数值] );
③:对象访问变量:
1.局部变量在其他类中年不能访问。
2.实例变量只能对象访问
3.静态变量类名访问,可以对象访问
4.实例变量在同一个类的多个对象之间不能数据共享
静态变量在同一个类的多个对象之间能数据共享
④:对象访问方法:
1.构造方法的访问--new
2.实例方法只能对象访问
3.静态方法类名访问,可以对象访问
4.有参数方法,需要传递参数【个数,类型】【要什么,给什么】
5.有返回值,方法体中的最后一句是return,返回的数据值要与方法的返回值类型匹配,
调用有返回值的方法需要定义变量来接收方法的返回值【给什么,收什么】
①:概念
SpringCloud 是一个服务治理平台,提供了一些服务框架。包含了:服务注册与发现、配置中心、消息中心、负载均衡、数据监控等,提供了全套的分布式系统解决方案。是一个基于 Spring Boot 实现的云应用开发工具,它为开发中的配置管理、服务发现、断路器、 智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。
②:常用组件
Spring Cloud Netflix Eureka :服务注册中心
Spring Cloud Netflix Ribbon :客户端负载均衡
Spring Cloud Netflix Hystrix: 服务容错保护
Spring Cloud Netflix Feign:声明式服务调用
Spring Cloud OpenFeign(可替代Feign)
Spring Cloud Netflix Zuul:API网关服务,过滤,安全,监控,限流,路由
Spring Cloud Gateway(可代替Zuul)
Spring Cloud Config: 分布式配置中心
Spring Cloud Bus:事件,消息总线
Spring Cloud Stream: 消息驱动微服务
Spring Cloud Sleuth: 分布式服务跟踪
Spring Cloud Alibaba: 阿里巴巴结合自身微服务实践,开源的微服务全家桶
随着互联网应用的发展,业务体量逐渐增大,那么原有的系统搭建就很难支撑起现有的业务体量。因此开始陆续出现应用与数据库分离、Nginx 反向代理、缓存组件、分组部署、RPC 分布式应用、网关服务、监控系统等等。
在这些系统的架构的演进过程中,不断的出现各类支撑起服务建设升级的系统和中间件。在中间件这一层的建设,基本是来自于业务系统中非业务逻辑的通用性核心功能抽离出来的,而逐步形成各类中间件服务。
中间件:是介于操作系统和应用软件之间,为应用软件提供服务功能的软件,有消息中间件,通信中间件,应用服务器等。由于介于两种软件之间,所以,称为中间件。
中间件屏蔽了底层操作系统的复杂性,让开发工程师可以把更多的专注力放在业务系统的逻辑和流程实现上,让开发人员面对的是一个简单、单一、统一的开发环境,减少程序设计因底层差异而导致的复杂度。
中间件最终带给系统的是交付质量和交付能力的提升,它的存在不只是让开发简单、周期短,更主要的是减少了系统上线后的不稳性和运维成本以及管理的工作量,同时还减少了服务资源的投入。
中间件主要分6类:
①:远程过程调用中间件
RPC 扩展了过程开发中的“功能调用和结果返回”机制,使得它可以适应于远程环境调用和分布式部署。
②:消息中间件
系统架构设计中使用消息中间件把应用扩展到不同的操作系统和不同的网络环境中,基于消息事件驱动机制处理业务逻辑的解耦和消峰。
③:交易中间件
是一种针对联机交易处理系统而设计的中间件,其实很多业务发起的中间件都属于此类。交易中间件就相当于一组程序模块,使用它可以大大的减少开发一个交易系统所需要的编程工作量。
④:对象中间件
是从业务系统中抽离出具有通用性通用功能的核心逻辑,面向对象的技术基本是 Java 语言的最大目标,通过封装、继承、多态,提供良好的代码重用性。
⑤:数据访问中间件
此类中间件适用于应用程序与各类数据源的通信操作,以便于直接访问和更新基于服务器的数据源,数据源可以是关系型、非关系型以及对象型,大概你可以想到的是 Mysql、Oracle、Redis 或者 Elasticsearch 等
⑥:终端仿真/屏幕转换中间件
例如;中继器、IO板卡、PLC等作为控制层进行高级语言与展示层的交互。