題型分佈及得分:問答題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. 向管理者提供統一的運維管理和大數據運營分析*