题型分布及得分:问答题5道40分;设计题3道50分;论述题10分
一、 问答题(40分 5道)
1. 阻塞的Socket通信的原理是什么?
Socket 就像一个电话插座,负责连通两端的电话,进行点对点通信,让电话可以进行通信,端口就像插座上的孔,端口不能同时被其他进程占用。而我们建立连接就像把插头插在这个插座上,创建一个 Socket 实例开始监听后,这个电话插座就时刻监听着消息的传入,谁拨通我这个“IP 地址和端口”,我就接通谁。
实际上,Socket 是在应用层和传输层之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信。Socket 起源于 UNIX,在 UNIX 一切皆文件的思想下,进程间通信就被冠名为文件描述符(file descriptor),Socket 是一种“打开—读/写—关闭”模式的实现,服务器和客户端各自维护一个“文件”,在建立连接打开后,可以向文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。
2. 阻塞通信中,阻塞在服务端和客户端发生的时机是什么?
- 服务端:accept 客户端:connect
- 读操作:当接收缓冲区中没有数据时,读操作会一直阻塞住,直到有数据到来才返回
- 写操作:发送缓冲区没有空间或者空间不足的话,写操作会直接阻塞住,直到缓冲区有足够的空间为止
3. 如何实现多个客户端的阻塞Socket通信?
- 服务器端创建ServerSocket,循环调用accept()等待客户端连接
- 客户端创建一个socket并请求和服务器端连接
- 服务器端接受客户端请求,创建socket与该客户建立专线连接
- 建立连接的两个socket在一个单独的线程上对话
- 服务器端继续等待新的连接
4. TCP和UDP通信的异同,各自有什么优缺点
【这个是我自己总结的】
1> TCP基于连接,UDP无连接
2> TCP对系统资源要求多,UDP少
3> TCP面向字节流,UDP面向报文
4> TCP连接为一对一,UDP支持一对一、一对多、多对一和多对多的交互通信
5> TCP保证数据正确性及顺序,UDP不保证
TCP优点: ① 稳定【无差错、不丢失、不重复、按序到达】
② 相对安全【三握四挥机制】
③ 逻辑通信信道为全双工
TCP缺点: ① 速度慢
② 资源开销大
UDP优点: ① 速度快
② 系统开销小
UDP缺点: ① 不可靠,不适用网络差的环境
5. 非阻塞通信的基本原理是什么,阻塞通信和非阻塞通信的区别?
在整个通信过程中读和写操作不会阻塞,当前处理线程不存在阻塞情况。从A机器到B机器它的通信过程是:A机器一条线程将通道设置为写事件后往下执行,而另外一条线程遍历到此通道有字节要写并往socket写数据,B机器一条线程遍历到此通道有字节要读,交给另外一条线程对socket读数据,处理完又把通道设置为写事件,遍历线程遍历到此通道有字节要写,又往socket写数据传往A机器,不断往下循环此操作直到完成通信。这个过程每台机器都有两类主要线程,一类是负责逻辑处理且将通道改为可写或可读事件的线程,另外一类是专门用于遍历通道并负责socket读写的线程,这种方式就是非阻塞IO模式
6. 什么是阿姆尔达加速比定律?什么是伪共享?如何解决伪共享的问题及方法?
- 阿姆尔达加速比定律:用并行前的执行速度和并行后的执行速度之比来表示的,它表示了在并行化之后的效率提升情况
- 伪共享:缓存系统中是以缓存行(cache line)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享
- 伪共享的解决方法:
- 字节填充:也就是创建一个变量的时候使用填充字段填充该变量所在的缓存行,这样就避免了多个变量存在同一个缓存行
- 使用sun.misc.Contended注解,用来解决伪共享问题
7. NIO 和传统的I0的区别是什么?有什么优点?
-
传统的IO特点
- 它的各种流是阻塞的。单线程的情况下,只能存在一个客户端。
- 一个线程调用读写的方法的时候,其它要调用其读写时会被阻塞,直到有一些数据被读取或者数据被完全写入。
- 它是面向流的。每次是从流中读取一个后者多个字节,直到所有的字节被读完。没有缓冲区,不能前后移动流中的数据。
- serverSocket和socket都是阻塞式的,因此一旦有大规模的并发行为,而每一个访问都会开启一个新线程。
-
NIO
-
NIO是为了弥补IO的不足而诞生的,性特性为:非阻塞I/O,选择器,缓冲以及管道。管道(Channel),缓冲(Buffer) ,选择器( Selector)是其主要特征
- buffer:因为NIO是基于缓冲的,所以buffer是最底层的必要类,这也是IO和NIO的根本不同,虽然stream等有buffer开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而NIO却是直接读到buffer中进行操作。
- channel:类似于IO的stream,但是不同的是除了FileChannel,其他的channel都能以非阻塞状态运行。FileChannel执行的是文件的操作,可以直接DMA操作内存而不依赖于CPU。其他比如socketchannel就可以在数据准备好时才进行调用。
- selector:用于分发请求到不同的channel,这样才能确保channel不处于阻塞状态就可以收发消息
-
在NIO中,数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。NIO的buffer可以使用直接内存缓冲区,该缓冲区不在JVM中,性能会比JVM的缓冲区略好,不过会增加相应的垃圾回收的负担,因为JVM缓冲区的性能已经足够好,所以除非在对缓冲有特别要求的地方使用直接缓冲区,尽量使用JVM缓冲。
-
8. 缓冲Buffer有如下的一些属性:容量、极限、位置,表示什么?另外,其关系是什么?如何复用?
- 容量:表示该缓冲区可以保存多少数据
- 极限:表示缓冲区的当前终点,不能对缓冲区中超过极限的区域进行读写操作。极限是可以修改的,这有利于缓冲区的重用。
- 位置:表示缓冲区中下一个读写单元的位置,每次读写缓冲区的数据时,都会改变该值,为下一次读写数据做准备。位置是一个非负数,不应该大于极限
- 以上三个属性的大小关系为:容量>=极限>=位置>=0
?????如何复用
9. 什么是静态代理?什么是动态代理?如何实现动态代理?
-
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
-
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
-
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
-
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
-
AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、 Hibernate 框架等等都是动态代理的使用例子。
10. Spring IOC和AOP是什么?各自想解决何种问题?
- IOC:控制反转,是一种设计模式。一是控制权的转移:由传统的在程序中控制并依赖转移到容器赖控制;第二是依赖注入:将相互以来的对象分离,在Spring配置文件中描述他们的依赖关系。他们的依赖关系只在使用的时候才建立。从而促进松耦合,面向接口编程、非侵入的以及类的注入。
- AOP:面向切面,是一种编程思想,oop的延续。将系统中非核心的业务提取出来,进行单独处理。将一个共同的功能从不同的类中分离出来,然后将其封装使之可以被其它类使用。面向方面编程是类的织入,比如把日志记录,性能分析,安全性分析,持久化等等织入到业务逻辑中去。
- Spring的AOP和IOC在项目中都是为了解决系统代码耦合度过高的问题。使代码重用度高,易于维护。比如事务,日志和安全等。
11. Mybatis 的持久性中,并没有DAO层的持久性类的实现,那么Mybatis是如何实现持久性的?
- 单纯的基于xml方式,可以没有接口,sql语句写在xml文件中
- 单纯的基于注解方式,可以没有xml文件,sql语句写在dao层接口的注解中
- xml方式和注解方式相结合的方式,sql语句写在xml文件中
12. Spring Boot的特点是什么?和传统的Spring MVC的结构相比有什么优势?
-
两者是不同的概念
-
SSM是WEB应用框架,涵盖整个应用层,而spring boot你可以看做一个启动、配置、快速开发的辅助框架,本身针对的是微服务。
-
springboot 只是为了提高开发效率,是为了提升生产力的
-
springboot一个应用是一个可执行jar(启动类main方法启动web应用),而不像传统的war,内嵌tomcat容器,可以jar形式启动一个服务,可以快速部署发布web服务,微服务最好不过了。
-
将原有的xml配置,简化为java配置
-
当然结构可能跟一般的ssm有一定区别,但其实主要是在资源文件。
-
Spring Boot 默认“约定”从资源目录的这些子目录读取静态资源,而且支持yml配置文件。
13. 什么是微服务?和单体架构、SOA 架构相比,微服务有何优缺点?
-
SOA(Service Oriented Architecture)“面向服务的架构”:他是一种设计方法,其中包含多个服务, 服务之间通过相互依赖最终提供一系列的功能。一个服务 通常以独立的形式存在与操作系统进程中。各个服务之间 通过网络调用。
-
微服务架构:其实和 SOA 架构类似,微服务是在 SOA 上做的升华,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”,原有的单个业务系统会拆分为多个可以独立开发、设计、运行的小应用。这些小应用之间通过服务完成交互和集成。
-
微服务架构的好处
- 单个服务很容易开发、理解和维护。
- 这种架构使得每个服务都可以有专门开发团队来开发。
- 微服务架构模式是每个微服务独立的部署。
- 微服务架构模式使得每个服务独立扩展。
-
微服务架构的不足
-
微服务应用是分布式系统,由此会带来固有的复杂性。
-
服务地址目录,服务健康度,部署困难,服务依赖问题,数据库分区问题。
-
二、设计题
1. Socket 的单客户端和多客户端的设计和实现。
单客户端服务器:
public static void main(String[] args)
{
try {
// 初始化服务端socket并且绑定9999端口
ServerSocket serverSocket =new ServerSocket(9999);//等待客户端的连接
Socket socket = serverSocket.accept();
//获取输入流,并且指定统一的编码格式
BufferedReader bufferedReader =new BufferedReader(new
InputStreamReader(socket.getInputStream(),"UTF-8")); //读取一行数据
String str;
//通过while循环不断读取信息
while ((str = bufferedReader.readLine())!=null){
//输出打印
System.out.println(str);
}
}catch (IOException e) {
e.printStackTrace();
}
}
多客户端服务器:
public class test {
public static void main(String[] args) throws IOException {
// 初始化服务端socket并且绑定9999端口
ServerSocket serverSocket = new ServerSocket(9999);
while (true) {
//等待客户端的连接
Socket socket = serverSocket.accept(); //每当有一个客户端连接进来后,就启动一个单独的线程进行处理
new Thread(new Runnable() {
@Override
public void run() {
//获取输入流,并且指定统一的编码格式
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
//读取一行数据
String str;
//通过while循环不断读取信息,
while ((str = bufferedReader.readLine()) != null) {
//输出打印
System.out.println("客户端说:" + str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
2. 线程池的工作线程的实现方法。
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int temp = i;
newFixedThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + ",i:" + temp);
}
});
}
3. 基于DatagramSocket的通信问题的设计和实现。
Java用两个类实现UDP:DatagramPacket和DatagramSocket,前者将数据字节填充到UDP包,后者收发UDP包。用法很简单,DatagramSocket收发DatagramPacket即可。与TCP不同,UDP的socket并没有客户端和服务端的区别
服务端
public class UDPServer {
public final static int PORT = 5555;
public static void main(String[] args) {
byte[] buffer = new byte[64];
//侦听某个UDP端口
try (DatagramSocket server = new DatagramSocket(PORT)) {
server.setSoTimeout(10000);
while (true) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
server.receive(packet);//10s超时接收
byte[] now = new Date().toString().getBytes("US-ASCII");
//组织数据,并传入请求方socket地址作为回应地址
DatagramPacket outgoingPacket = new DatagramPacket(now, now.length, packet.getAddress(), packet.getPort());
server.send(outgoingPacket);
} catch (SocketTimeoutException ex) {
System.out.println("Timeout!");//超时
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (SocketException ex) {
ex.printStackTrace();
}
}
}
客户端:
public class UDPClient {
public final static int PORT = 5555;
public final static String HOST = "localhost";
public static void main(String[] args) {
try (DatagramSocket socket = new DatagramSocket()) {//绑定端口由系统分配
//DatagramSocket依赖于数据报DatagramPacket收发数据,byte[1]是为了触发服务端
DatagramPacket packet = new DatagramPacket(new byte[1], 1, InetAddress.getByName(HOST), PORT);
socket.setSoTimeout(10000);//receive数据阻塞等待10s,超过这个时间仍未收到数据说明丢失了
socket.send(packet);
DatagramPacket incomePacket = new DatagramPacket(new byte[64], 64);
try {
socket.receive(incomePacket);//阻塞接收,10s超时
byte[] nowByte = new byte[incomePacket.getLength()];
System.arraycopy(incomePacket.getData(), 0, nowByte, 0, incomePacket.getLength());
String now = new String(nowByte, "US-ASCII");
System.out.println(now);
} catch (SocketTimeoutException ex) {
System.out.println("Timeout!");
}
} catch (IOException ex) {
return;
}
}
}
4. 基于通道的非阻塞通信的服务端和客户端的设计与实现。
server
public class NIOServer1 {
// 本地字符集
private static final String LocalCharSetName = "UTF-8";
// 本地服务器监听的端口
private static final int Listenning_Port = 8888;
// 缓冲区大小
private static final int Buffer_Size = 1024;
// 超时时间,单位毫秒
private static final int TimeOut = 3000;
public static void main(String[] args) throws IOException {
// 创建一个在本地端口进行监听的服务Socket信道.并设置为非阻塞方式
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(Listenning_Port));
serverChannel.configureBlocking(false);
// 创建一个选择器并将serverChannel注册到它上面
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 等待某个信道就绪
if (selector.select(TimeOut) == 0) {
System.out.println(".");
continue;
}
// 获得就绪信道的键迭代器
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
// 使用迭代器进行遍历就绪信道
while (keyIter.hasNext()) {
SelectionKey key = keyIter.next();
// 这种情况是有客户端连接过来,准备一个clientChannel与之通信
if (key.isAcceptable()) {
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ,
ByteBuffer.allocate(Buffer_Size));
}
// 客户端有写入时
if (key.isReadable()) {
// 获得与客户端通信的信道
SocketChannel clientChannel = (SocketChannel) key.channel();
// 得到并重置缓冲区的主要索引值
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
// 读取信息获得读取的字节数
long bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// 没有读取到内容的情况
clientChannel.close();
} else {
// 将缓冲区准备为数据传出状态
buffer.flip();
// 将获得字节字符串(使用Charset进行解码)
String receivedString = Charset
.forName(LocalCharSetName).newDecoder().decode(buffer).toString();
// 控制台打印出来
System.out.println("接收到信息:" + receivedString);
// 准备发送的文本
String sendString = "你好,客户端. 已经收到你的信息" + receivedString;
// 将要发送的字符串编码(使用Charset进行编码)后再进行包装
buffer = ByteBuffer.wrap(sendString.getBytes(LocalCharSetName));
// 发送回去
clientChannel.write(buffer);
// 设置为下一次读取或是写入做准备
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}
keyIter.remove();
}
}
}
}
client
public class NIOClient1Test {
public static void main(String[] args) throws UnknownHostException,
IOException {
Socket s = new Socket("localhost", 8888);
InputStream inStream = s.getInputStream();
OutputStream outStream = s.getOutputStream();
// 输出
PrintWriter out = new PrintWriter(outStream, true);
out.println("getPublicKey你好!");
out.flush();
s.shutdownOutput();// 输出结束
// 输入
Scanner in = new Scanner(inStream);
StringBuilder sb = new StringBuilder();
while (in.hasNextLine()) {
String line = in.nextLine();
sb.append(line);
}
String response = sb.toString();
System.out.println("response=" + response);
}
}
5. 求和、求pi、排序问题的并行(多线程)的实现。
6. 静态代理类的设计和实现。(计算)
7. 动态代理类的设计与实现。(计算)
8. 数据库连接池DBConnectionPool的实现。
三、论述题
1. 你使用过哪些框架?这些框架的优缺点是什么?你在何处使用过这些框架?
使用过spring mvc 和 mybaits ,spring boot,spring等。
spring mvc优点:
- 使用简单,学习成本低。
- 很容易就可以写出性能优秀的程序.。
- 灵活性强,Spring MVC的框架易扩展SpringMVC的。
缺点:
- Spring与MVC 的Servlet API 耦合,难以脱离容器独立运行
- 太过于细分,开发效率低
- 过度追求完美,有过度设计的危险解决的问题
mybatis优点:
- 易于上手和掌握。
- sql写在xml里,便于统一管理和优化。
- 解除sql与程序代码的耦合。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
缺点:
- sql工作量很大,尤其是字段多、关联表多时,更是如此。
- sql依赖于数据库,导致数据库移植性差。
- 由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。
- 字段映射标签和对象关系映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。(比如配置了一对多Collection标签,如果sql里没有join子表或查询子表的话,查询后返回的对象是不具备对象关系的,即Collection的对象为null)
- DAO层过于简单,对象组装的工作量较大。
- 不支持级联更新、级联删除。
- 编写动态sql时,不方便调试,尤其逻辑复杂时。 8 提供的写动态sql的xml标签功能简单(连struts都比不上),编写动态sql仍然受限,且可读性低。
- 参数的数据类型支持不完善。(如参数为Date类型时,容易报没有get、set方法,需在参数上加@param)
spring boot优点
- 快速构建项目
- 对主流开发框架的无配置集成
- 项目可独立运行,无需外部依赖 Servlet 容器
- .提供运行时的应用监控
- 极大地提高了开发、部署效率
- 与云计算的天然集成
缺点:
- 版本迭代速度很快,一些模块改动很大
- .由于不用自己做配置,报错时很难定位
- 网上现成的解决方案比较少
sping优点
-
提供了一种管理对象的方法,可以把中间层对象有效地组织起来。一个完美的框架“黏合剂”。
-
采用了分层结构,可以增量引入到项目中。
-
有利于面向接口编程习惯的养成。
-
目的之一是为了写出易于测试的代码。
-
非侵入性,应用程序对Spring API的依赖可以减至最小限度。
-
一致的数据访问介面。
-
一个轻量级的架构解决方案。
缺点:
- 中断了应用程序的逻辑,使代码变得不完整,不直观。此时单从Source无法完全把握应用的所有行为。
- 将原本应该代码化的逻辑配置化,增加了出错的机会以及额外的负担。
- 调试阶段不直观,后期的bug对应阶段,不容易判断问题所在。
使用:
2. 云平台当中的Saas、PaaS、 las 是什么?你对此的理解。
IaaS: Infrastructure-as-a-Service(基础设施即服务)
第一层叫做IaaS,有时候也叫做Hardware-as-a-Service,几年前如果你想在办公室或者公司的网站上运行一些企业应用,你需要去买服务器,或者别的高昂的硬件来控制本地应用,让你的业务运行起来。
但是现在有IaaS,你可以将硬件外包到别的地方去。IaaS公司会提供场外服务器,存储和网络硬件,你可以租用。节省了维护成本和办公场地,公司可以在任何时候利用这些硬件来运行其应用。
一些大的IaaS公司包括Amazon, Microsoft, VMWare, Rackspace和Red Hat.不过这些公司又都有自己的专长,比如Amazon和微软给你提供的不只是IaaS,他们还会将其计算能力出租给你来host你的网站。
PaaS: Platform-as-a-Service(平台即服务)
第二层就是所谓的PaaS,某些时候也叫做中间件。你公司所有的开发都可以在这一层进行,节省了时间和资源。
PaaS公司在网上提供各种开发和分发应用的解决方案,比如虚拟服务器和操作系统。这节省了你在硬件上的费用,也让分散的工作室之间的合作变得更加容易。网页应用管理,应用设计,应用虚拟主机,存储,安全以及应用开发协作工具等。
一些大的PaaS提供者有Google App Engine,Microsoft Azure,Force.com,Heroku,Engine Yard。最近兴起的公司有AppFog, Mendix 和 Standing Cloud
SaaS: Software-as-a-Service(软件即服务)
第三层也就是所谓SaaS。这一层是和你的生活每天接触的一层,大多是通过网页浏览器来接入。任何一个远程服务器上的应用都可以通过网络来运行,就是SaaS了。
你消费的服务完全是从网页如Netflix, MOG, Google Apps, Box.net, Dropbox或者苹果的iCloud那里进入这些分类。尽管这些网页服务是用作商务和娱乐或者两者都有,但这也算是云技术的一部分。
一些用作商务的SaaS应用包括Citrix的GoToMeeting,Cisco的WebEx,Salesforce的CRM,ADP,Workday和SuccessFactors。
3. 阅读以下材料,设计面向广大企业用户的物联网平台,给出你的理解和方案。
本项目通过搭建XXX物联网云平台,为企业用户的能源提供机房服务、计算服务、存储服务、备份服务、安全服务、网络服务、管理平台服务。系统迁移,支持服务和应用集中部署的服务能力。主要内容如下:
a. 搭建xx物联云平台,…
b. 向用户提供统一的物联网系统应用,如用户管理、系统监控、设备管理、环境管理、能源信息展示等
c. 向管理者提供统一的运维管理和大数据运营分析*