高性能Server---Reactor模型

無處不在的C/S架構

在這個充斥著云的時代,我們使用的軟件可以說99%都是C/S架構的!

  • 你發郵件用的Outlook,Foxmail等
  • 你看視頻用的優酷,土豆等
  • 你寫文檔用的Office365,googleDoc,Evernote等
  • 你瀏覽網頁用的IE,Chrome等(B/S是特殊的C/S)
  • ……

C/S架構的軟件帶來的一個明顯的好處就是:只要有網絡,你可以在任何地方干同一件事。

例如:你在家里使用Office365編寫了文檔。到了公司,只要打開編輯地址就可以看到在家里編寫的文檔,進行展示或者繼續編輯。甚至在手機上進行閱讀與編輯。不再需要U盤拷來拷去了。

C/S架構可以抽象為如下模型:

  • C就是Client(客戶端),上面的B是Browser(瀏覽器)
  • S就是Server(服務器):服務器管理某種資源,并且通過操作這種資源來為它的客戶端提供某種服務

C/S架構之所以能夠流行的一個主要原因就是網速的提高以及費用的降低,特別是無線網絡速度的提高。試想在2G時代,大家最多就是看看文字網頁,小說什么的。看圖片,那簡直就是奢侈!更別說看視頻了!

網速的提高,使得越來越多的人使用網絡,例如:優酷,微信都是上億用戶量,更別說天貓雙11的瞬間訪問量了!這就對服務器有很高的要求!能夠快速處理海量的用戶請求!那服務器如何能快速的處理用戶的請求呢?

高性能服務器

高性能服務器至少要滿足如下幾個需求:

  • 效率高:既然是高性能,那處理客戶端請求的效率當然要很高了
  • 高可用:不能隨便就掛掉了
  • 編程簡單:基于此服務器進行業務開發需要足夠簡單
  • 可擴展:可方便的擴展功能
  • 可伸縮:可簡單的通過部署的方式進行容量的伸縮,也就是服務需要無狀態

而滿足如上需求的一個基礎就是高性能的IO!

Socket

無論你是發郵件,瀏覽網頁,還是看視頻~實際底層都是使用的TCP/IP,而TCP/IP的編程抽象就是Socket!

我一直對Socket的中文翻譯很困惑,個人覺得是我所接觸的技術名詞翻譯里最莫名其妙的,沒有之一!

Socket中文翻譯為”套接字”!什么鬼?在很長的時間里我都無法將其和網絡編程關聯上!后來專門找了一些資料,最后在知乎上找到了一個還算滿意的答案(具體鏈接,請見文末的參考資料鏈接)!

Socket的原意是插口,想表達的意思是插口與插槽的關系!”send socket”插到”receive socket”里,建立了鏈接,然后就可以通信了!

套接字的翻譯,應該是參考了套接管(如下圖)!從這個層面上來看,是有那么點意思!

套接字這個翻譯已經是標準了,不糾結這個了!

我們看一下Socket之間建立鏈接及通信的過程!實際上就是對TCP/IP連接與通信過程的抽象:

  • 服務端Socket會bind到指定的端口上,Listen客戶端的”插入”
  • 客戶端Socket會Connect到服務端
  • 當服務端Accept到客戶端連接后
  • 就可以進行發送與接收消息了
  • 通信完成后即可Close

對于IO來說,我們聽得比較多的是:

  • BIO:阻塞IO
  • NIO:非阻塞IO
  • 同步IO
  • 異步IO

以及其組合:

  • 同步阻塞IO
  • 同步非阻塞IO
  • 異步阻塞IO
  • 異步非阻塞IO

那么什么是阻塞IO、非阻塞IO、同步IO、異步IO呢?

  • 一個IO操作其實分成了兩個步驟:發起IO請求和實際的IO操作
  • 阻塞IO和非阻塞IO的區別在于第一步:發起IO請求是否會被阻塞,如果阻塞直到完成那么就是傳統的阻塞IO;如果不阻塞,那么就是非阻塞IO
  • 同步IO和異步IO的區別就在于第二個步驟是否阻塞,如果實際的IO讀寫阻塞請求進程,那么就是同步IO,因此阻塞IO、非阻塞IO、IO復用、信號驅動IO都是同步IO;如果不阻塞,而是操作系統幫你做完IO操作再將結果返回給你,那么就是異步IO

舉個不太恰當的例子 :比如你家網絡斷了,你打電話去中國電信報修!

  • 你撥號—客戶端連接服務器
  • 電話通了—連接建立
  • 你說:“我家網斷了,幫我修下”—發送消息
  • 說完你就在那里等,那么就是阻塞IO
  • 如果正好你有事,你放下帶電話,然后處理其他事情了,過一會你來問下,修好了沒—那就是非阻塞IO
  • 如果客服說:“馬上幫你處理,你稍等”—同步IO
  • 如果客服說:“馬上幫你處理,好了通知你”,然后掛了電話—異步IO

本文只討論BIO和NIO,AIO使用度沒有前兩者普及,暫不討論!

下面從代碼層面看看BIO與NIO的流程!

BIO

  • 客戶端代碼
1
2
3
4
5
6
7
8
9
10
//Bind,Connect
Socket client = new Socket("127.0.0.1",7777);
//讀寫
PrintWriter pw = new PrintWriter(client.getOutputStream());
BufferedReader br=
new BufferedReader(new InputStreamReader(System.in));
pw.write(br.readLine());
//Close
pw.close();
br.close();
  • 服務端代碼
1
2
3
4
5
6
7
8
9
10
11
Socket socket;
//Bind,Listen
ServerSocket ss = new ServerSocket(7777);
while (true) {
//Accept
socket = ss.accept();
//一般新建一個線程執行讀寫
BufferedReader br = new BufferedReader(
new InputStreamReader(socket .getInputStream()));
System.out.println("you input is : " + br.readLine());
}
  • 上面的代碼可以說是學習Java的Socket的入門級代碼了
  • 代碼流程和前面的圖可以一一對上

模型圖如下所示:

BIO優缺點

  • 優點
    • 模型簡單
    • 編碼簡單
  • 缺點
    • 性能瓶頸低

優缺點很明顯。這里主要說下缺點:主要瓶頸在線程上。每個連接都會建立一個線程。雖然線程消耗比進程小,但是一臺機器實際上能建立的有效線程有限,以Java來說,1.5以后,一個線程大致消耗1M內存!且隨著線程數量的增加,CPU切換線程上下文的消耗也隨之增加,在高過某個閥值后,繼續增加線程,性能不增反降!而同樣因為一個連接就新建一個線程,所以編碼模型很簡單!

就性能瓶頸這一點,就確定了BIO并不適合進行高性能服務器的開發!像Tomcat這樣的Web服務器,從7開始就從BIO改成了NIO,來提高服務器性能!

NIO

  • NIO客戶端代碼(連接)
1
2
3
4
5
6
7
8
//獲取socket通道
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
//獲得通道管理器
selector=Selector.open();
channel.connect(new InetSocketAddress(serverIp, port));
//為該通道注冊SelectionKey.OP_CONNECT事件
channel.register(selector, SelectionKey.OP_CONNECT);
  • NIO客戶端代碼(監聽)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
while(true){
//選擇注冊過的io操作的事件(第一次為SelectionKey.OP_CONNECT)
selector.select();
while(SelectionKey key : selector.selectedKeys()){
if(key.isConnectable()){
SocketChannel channel=(SocketChannel)key.channel();
if(channel.isConnectionPending()){
channel.finishConnect();//如果正在連接,則完成連接
}
channel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable()){ //有可讀數據事件。
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(10);
channel.read(buffer);
byte[] data = buffer.array();
String message = new String(data);
System.out.println("recevie message from server:, size:"
+ buffer.position() + " msg: " + message);
}
}
}
  • NIO服務端代碼(連接)
1
2
3
4
5
6
7
8
//獲取一個ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
//獲取通道管理器
selector = Selector.open();
//將通道管理器與通道綁定,并為該通道注冊SelectionKey.OP_ACCEPT事件,
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  • NIO服務端代碼(監聽)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
while(true){
//當有注冊的事件到達時,方法返回,否則阻塞。
selector.select();
for(SelectionKey key : selector.selectedKeys()){
if(key.isAcceptable()){
ServerSocketChannel server =
(ServerSocketChannel)key.channel();
SocketChannel channel = server.accept();
channel.write(ByteBuffer.wrap(
new String("send message to client").getBytes()));
//在與客戶端連接成功后,為客戶端通道注冊SelectionKey.OP_READ事件。
channel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable()){//有可讀數據事件
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(10);
int read = channel.read(buffer);
byte[] data = buffer.array();
String message = new String(data);
System.out.println("receive message from client, size:"
+ buffer.position() + " msg: " + message);
}
}
}

NIO模型示例如下:

  • Acceptor注冊Selector,監聽accept事件
  • 當客戶端連接后,觸發accept事件
  • 服務器構建對應的Channel,并在其上注冊Selector,監聽讀寫事件
  • 當發生讀寫事件后,進行相應的讀寫處理

NIO優缺點

  • 優點
    • 性能瓶頸高
  • 缺點
    • 模型復雜
    • 編碼復雜
    • 需處理半包問題

NIO的優缺點和BIO就完全相反了!性能高,不用一個連接就建一個線程,可以一個線程處理所有的連接!相應的,編碼就復雜很多,從上面的代碼就可以明顯體會到了。還有一個問題,由于是非阻塞的,應用無法知道什么時候消息讀完了,就存在了半包問題!

半包問題

簡單看一下下面的圖就能理解半包問題了!

我們知道TCP/IP在發送消息的時候,可能會拆包(如上圖1)!這就導致接收端無法知道什么時候收到的數據是一個完整的數據。例如:發送端分別發送了ABC,DEF,GHI三條信息,發送時被拆成了AB,CDRFG,H,I這四個包進行發送,接受端如何將其進行還原呢?在BIO模型中,當讀不到數據后會阻塞,而NIO中不會!所以需要自行進行處理!例如,以換行符作為判斷依據,或者定長消息發生,或者自定義協議!

NIO雖然性能高,但是編碼復雜,且需要處理半包問題!為了方便的進行NIO開發,就有了Reactor模型!

Reactor模型

  • AWT Events

Reactor模型和AWT事件模型很像,就是將消息放到了一個隊列中,通過異步線程池對其進行消費!

Reactor中的組件

  • Reactor:Reactor是IO事件的派發者。
  • Acceptor:Acceptor接受client連接,建立對應client的Handler,并向Reactor注冊此Handler。
  • Handler:和一個client通訊的實體,按這樣的過程實現業務的處理。一般在基本的Handler基礎上還會有更進一步的層次劃分, 用來抽象諸如decode,process和encoder這些過程。比如對Web Server而言,decode通常是HTTP請求的解析, process的過程會進一步涉及到Listener和Servlet的調用。業務邏輯的處理在Reactor模式里被分散的IO事件所打破, 所以Handler需要有適當的機制在所需的信息還不全(讀到一半)的時候保存上下文,并在下一次IO事件到來的時候(另一半可讀了)能繼續中斷的處理。為了簡化設計,Handler通常被設計成狀態機,按GoF的state pattern來實現。

對應上面的NIO代碼來看:

  • Reactor:相當于有分發功能的Selector
  • Acceptor:NIO中建立連接的那個判斷分支
  • Handler:消息讀寫處理等操作類

Reactor從線程池和Reactor的選擇上可以細分為如下幾種:

Reactor單線程模型

這個模型和上面的NIO流程很類似,只是將消息相關處理獨立到了Handler中去了!

雖然上面說到NIO一個線程就可以支持所有的IO處理。但是瓶頸也是顯而易見的!我們看一個客戶端的情況,如果這個客戶端多次進行請求,如果在Handler中的處理速度較慢,那么后續的客戶端請求都會被積壓,導致響應變慢!所以引入了Reactor多線程模型!

Reactor多線程模型

Reactor多線程模型就是將Handler中的IO操作和非IO操作分開,操作IO的線程稱為IO線程,非IO操作的線程稱為工作線程!這樣的話,客戶端的請求會直接被丟到線程池中,客戶端發送請求就不會堵塞!

但是當用戶進一步增加的時候,Reactor會出現瓶頸!因為Reactor既要處理IO操作請求,又要響應連接請求!為了分擔Reactor的負擔,所以引入了主從Reactor模型!

主從Reactor模型

主Reactor用于響應連接請求,從Reactor用于處理IO操作請求!

Netty

Netty是一個高性能NIO框架,其是對Reactor模型的一個實現!

  • Netty客戶端代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
  • Netty Client Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf m = (ByteBuf) msg;
try {
long currentTimeMillis =
(m.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
} finally {
m.release();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
  • Netty服務端代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
  • Netty Server Handler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(final ChannelHandlerContext ctx) {
final ByteBuf time = ctx.alloc().buffer(4);
time.writeInt((int)
(System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time);
f.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
assert f == future;
ctx.close();
}
});
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}

我們從Netty服務器代碼來看,與Reactor模型進行對應!

  • EventLoopGroup就相當于是Reactor,bossGroup對應主Reactor,workerGroup對應從Reactor
  • TimeServerHandler就是Handler
  • child開頭的方法配置的是客戶端channel,非child開頭的方法配置的是服務端channel

具體Netty內容,請訪問Netty官網!

Netty的問題

Netty開發中一個很明顯的問題就是回調,一是打破了線性編碼習慣,
二就是Callback Hell!

看下面這個例子:

1
2
3
a.doing1(); //1
a.doing2(); //2
a.doing3(); //3

1,2,3處代碼如果是同步的,那么將按順序執行!但是如果不是同步的呢?我還是希望2在1之后執行,3在2之后執行!怎么辦呢?想想AJAX!我們需要寫類似如下這樣的代碼!

1
2
3
4
5
6
7
8
9
a.doing1(new Callback(){
public void callback(){
a.doing2(new Callback(){
public void callback(){
a.doing3();
}
})
}
});

那有沒有辦法解決這個問題呢?其實不難,實現一個類似Future的功能!當Client獲取結果時,進行阻塞,當得到結果后再繼續往下走!實現方案,一個就是使用鎖了,還有一個就是使用RingBuffer。經測試,使用RingBuffer比使用鎖TPS有2000左右的提高!

參考資料

  • Socket為什么要翻譯成套接字?
  • Reactor論文
  • Doug Lea《Scalable IO in Java》
  • Netty源碼
  • 剖析Disruptor:為什么會這么快?
  • 剖析Disruptor:為什么會這么快?(中文)
  • Java SE1.6中的Synchronized
  • 線程安全的無鎖RingBuffer實現
  • Java NIO類庫Selector機制解析(上)
  • Java NIO類庫Selector機制解析(下)

轉載于:https://www.cnblogs.com/he-px/p/7368501.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282692.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282692.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282692.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算機控制系統的試題,計算機控制系統練習題(1)

21. 給出多通道復用一個D/A轉換器的原理示意圖。 答&#xff1a;22. 什么是信號重構&#xff1f;答&#xff1a;把離散信號變為連續信號的過程&#xff0c;稱為信號重構&#xff0c;它是采樣的逆過程。23. 寫出零階保持器的傳遞函數&#xff0c;引入零階保持器對系統開環傳遞函…

springmvc_3(將數據放入map中)

jsp頁面 結果 轉載于:https://www.cnblogs.com/mohehpc/p/6491376.html

怎樣用原生js配合css的transition寫個無縫滾動

之所以想要寫原生js配合css轉換的無縫滾動&#xff0c;是因為之前在簡書上看到一哥們寫的一篇文章&#xff0c;說是在網上找了一堆js配合css transition屬性寫的輪播插件&#xff0c;可惜沒有無縫的效果&#xff0c;結果他用原生js重寫了一個可以無縫滾動的。好吧&#xff0c;我…

聊聊策略模式

1、簡介策略模式就是把各個平等的具體實現進行抽象、封裝成為獨立的算法類&#xff0c;然后通過上下文和具體的算法類來進行交互。各個策略算法都是平等的&#xff0c;地位是一樣的&#xff0c;正是由于各個算法的平等性&#xff0c;所以它們才是可以相互替換的。雖然我們可以動…

小學計算機課每周幾節,小學信息技術課時多少

滿意答案小學信息技術課程標準一、課程任務和教學目標中小學信息技術課程的主要任務是&#xff1a;培養學生對信息技術的興趣和意識&#xff0c;讓學生了解和掌握信息技術基本知識和技能&#xff0c;了解信息技術的發展及其應用對人類日常生活和科學技術的深刻影響。通過信息技…

張旭升20162329 2006-2007-2 《Java程序設計》第一周學習總結

20162329 2006-2007-2 《Java程序設計》第一周學習總結 教材學習內容總結 通過打書上的代碼熟悉了Java編程的基本過程 教材學習中的問題和解決過程 1.因為我的虛擬機不可用所以我在Windows中安裝了bash和git&#xff0c;但是由于Windows下bash中沒有中文而且我英語又不是很好所…

《圖解 HTTP》讀書筆記(未完待續)

ARP 協議&#xff08;Address Resolution Protocol&#xff09;一種以解析地址的協議&#xff0c;根據通信雙方的 IP 地址就可以查出對應的 MAC 地址。MAC&#xff08; Media Access Control Address&#xff09;地址是指網卡所屬的固定的地址MIME&#xff0c;多部分對象集合&a…

SQL查詢的安全方案

1.使用預處理語句防sql注入 2.寫入數據庫的數據要進行特殊字符轉義 3.錯誤信息不返回給用戶,記錄到日志 4.定期做數據備份 5.不給查詢用戶root權限,合理分配權限 6.關閉遠程訪問數據庫權限 7.修改root口令,不使用默認口令,使用較復雜口令 8.刪除多余的用戶 9.改變root用戶的名稱…

.NET 實現啟動時重定向程序運行路徑及 Windows 服務運行模式部署

日常工作中有時候會遇到需要將程序直接在服務器上運行&#xff0c;而不依賴于 IIS 托管的情況&#xff0c;直接運行有兩種方式&#xff0c;一種是部署為 服務模式&#xff0c;另一種則是 直接啟動 .NET 發布之后的 exe 文件以 控制臺模式運行&#xff0c;控制臺模式運行主要問題…

iOS runtime實戰應用:關聯對象

在開始之前建議先閱讀iOS runtime的基礎理解篇&#xff1a;iOS內功篇&#xff1a;runtime 有筒子在面試的時候&#xff0c;遇到這樣一個問題&#xff1a;“如何給NSArray添加一個屬性&#xff08;不能使用繼承&#xff09;”&#xff0c;筒子立馬蒙逼了&#xff0c;不能用繼承&…

黑龍江科技大學計算機考研復試科目,2020年黑龍江科技大學計算機應用技術考研經驗分享...

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓育明考研備考策略隨著IT業的迅猛發展&#xff0c;各高校計算機專業報名火爆&#xff0c;甚至文科學生跨專業報考時都會選擇計算機。計算機專業競爭日趨激烈&#xff0c;那么如何在充分發揮公共科目優勢的同時&#xff0c;盡量縮小專…

Mysql數據庫安全性問題【防注入】

一、SQL注入實例 后臺的插入語句代碼&#xff1a; $unsafe_variable $_POST[user_input]; mysql_query("INSERT INTO table (column) VALUES ($unsafe_variable)"); 當POST的內容為&#xff1a; value); DROP TABLE table;--以上的整個SQL查詢語句變成&#xff1…

Unexpected end of JSON input while parsing near錯誤解決方式(網上的方法)

原本是想創建一個create-react-app來著&#xff0c;但是在創建的中間會出現Unexpected end of JSON input while parsing near... 的錯誤。 在網上找到了一些方法&#xff0c;首先是清空npm的緩存。 npm cache clean --force 氮素&#xff0c;然并卵。near后面的內容變化了一下…

解決Qt5 Creator無法切換輸入法(fcitx),Ubuntu中不能使用搜狗輸入法錄入漢字問題...

2016年6月8日修正&#xff0c;ubuntu 16.04 Qt5.7.0 以及 Qt5.6.1均測試通過在Qt5.3之前&#xff0c;我發布過解決辦法 解決Qt5 Creator無法切換輸入法&#xff08;fcitx&#xff09;&#xff0c;不能錄入漢字問題&#xff0c;Qt5.4以及Qt5.5&#xff0c;舊辦法失效&#xff0c…

目前市場上用于個人計算機的硬盤尺寸是,第5章-硬盤(計算機組裝與維護).docx

ADDIN CNKISM.UserStyle一、選擇題1.磁盤存儲器的主要技術指標有多項&#xff0c;下面不屬于硬盤指標的是( )。A.存儲容量B.單碟容量C.轉速D.帶寬2.硬盤的平均尋道時間通常以毫秒為單位測量&#xff0c;是指( )。A.磁頭從一個柱面移到另一個隨機距離遠的柱面所需的平均時間B.…

Xmemcached學習筆記一(安裝memcached)

memcached有三種java客戶端 第一種&#xff1a;Com.danga 包下面的memcached&#xff0c;需引入jar(本人用的是memcached-2.5.2.jar 文末附上附件需要的可以下載) 第二種&#xff1a;spyMemcached 第三種&#xff1a;XMemcached 據說第三種是使用最簡單&#xff0c;最好用的&a…

WrapPanel 實現虛擬化

WrapPanel 實現虛擬化控件名&#xff1a;VirtualizingWrapPanel作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&#xff1b;眾…

pdo連接mysql數據庫(簡潔明了)

一 實例化pdo對象 $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root);二 數據查詢 1、如果不根據用戶傳過來的值進行操作,可以直接query sql $dsn "mysql:dbnametest;host127.0.0.1"; $pdo new PDO($dsn,root,root); $sql &qu…

一次微信小程序的快速開發體驗

起因 事情是這樣的 一天早上組里還早激烈的討論某個項目的可用性和發展前景&#xff0c;突然老大說了句&#xff0c;能不能做個小程序的版本呢&#xff1f;然后大家紛紛討論起來&#xff0c;有反對有支持&#xff0c;我就說了一句&#xff0c;剛出來的時候搞過一會。。。然后就…

造數據時踏過的坑

1.在產生隨機數時,在數據規模很大的時候很難出現自己要的模型,比如某個條件的數據量,此時要寫一個方法,來造一批這樣的數據 2.將控制數量,文件路徑寫成配置文件的形式,以免重復打包 3.輸入輸出文件夾,可以配置以免重復打包 轉載于:https://www.cnblogs.com/rocky-AGE-24/p/7376…