處理大併發之四 libevent demo詳細分析(對比epoll)
處理大併發之四 libevent demo詳細分析(對比epoll)
libevent預設情況下是單執行緒,每個執行緒有且僅有一個event_base,對應一個struct event_base結構體,以及賦予其上的事件管理器,用來安排託管給它的一系列的事件。
當有一個事件發生的時候,event_base會在合適的時間去呼叫繫結在這個事件上的函式,直到這個函式執行完成,然後在返回安排其他事件。需要注意的是:合適的時間並不是立即。
例如:
[cpp] view plain copy print?- struct event_base *base;
- base = event_base_new();//初始化libevent
struct event_base *base;
base = event_base_new();//初始化libevent
event_base_new對比epoll,可以理解為epoll裡的epoll_create。
event_base內部有一個迴圈,迴圈阻塞在epoll呼叫上,當有一個事件發生的時候,才會去處理這個事件。其中,這個事件是被繫結在event_base上面的,每一個事件就會對應一個struct event,可以是監聽的fd。
其中struct event 使用event_new 來建立和繫結,使用event_add來啟用,例如:
[cpp] view plain copy- struct event *listener_event;
- listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
struct event *listener_event;
listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
引數說明:
base:event_base型別,event_base_new的返回值
listener:監聽的fd,listen的fd
EV_READ|EV_PERSIST:事件的型別及屬性
do_accept:繫結的回撥函式
(void*)base:給回撥函式的引數
event_add(listener_event, NULL);
對比epoll:
event_new相當於epoll中的epoll_wait,其中的epoll裡的while迴圈,在libevent裡使用event_base_dispatch。
event_add相當於epoll中的epoll_ctl,引數是EPOLL_CTL_ADD,新增事件。
注:libevent支援的事件及屬性包括(使用bitfield實現,所以要用 | 來讓它們合體)EV_TIMEOUT: 超時EV_READ: 只要網路緩衝中還有資料,回撥函式就會被觸發EV_WRITE: 只要塞給網路緩衝的資料被寫完,回撥函式就會被觸發EV_SIGNAL: POSIX訊號量EV_PERSIST: 不指定這個屬性的話,回撥函式被觸發後事件會被刪除EV_ET: Edge-Trigger邊緣觸發,相當於EPOLL的ET模式
事件建立新增之後,就可以處理髮生的事件了,相當於epoll裡的epoll_wait,在libevent裡使用event_base_dispatch啟動event_base迴圈,直到不再有需要關注的事件。
有了上面的分析,結合之前做的epoll服務端程式,對於一個伺服器程式,流程基本是這樣的:
1. 建立socket,bind,listen,設定為非阻塞模式
2. 建立一個event_base,即
[cpp] view plain copy print?- struct event_base * event_base_new(void)
struct event_base * event_base_new(void)
3. 建立一個event,將該socket託管給event_base,指定要監聽的事件型別,並繫結上相應的回撥函式(及需要給它的引數)。即
[cpp] view plain copy print?- struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
4. 啟用該事件,即
[cpp] view plain copy print?- int event_add(struct event *ev, conststruct timeval *tv)
int event_add(struct event *ev, const struct timeval *tv)
5. 進入事件迴圈,即
[cpp] view plain copy print?- int event_base_dispatch(struct event_base *event_base)
int event_base_dispatch(struct event_base *event_base)
首先來翻譯下例子上面的一段話:
對於select函式來說,不同的作業系統有不同的代替函式,它包括:poll,epoll,kqueue,evport和/dev/poll。這些函式的效能都比select要好,其中epoll在IO中新增,刪除,通知socket準備好方面效能複雜度為O(1)。
不幸的是,沒有一個有效的介面是一個普遍存在的標準,Linux下有epoll,BSDS有kqueue,Solaris 有evport和/dev/poll,等等。沒有任何一個作業系統有它們中所有的,所以如果你想做一個輕便的高效能的非同步應用程式,你就需要把這些介面抽象的封裝起來,並且無論哪一個系統使用它都是最高效的。
這對於你來說就是最低階的libevent API,它提供了統一的介面取代了select,當它在計算機上執行的時候,使用了最有效的版本。
這裡是ROT13伺服器的另外一個版本,這次,他使用了libevent代替了select。這意味著我們不再使用fd_sets,取而代之的使用event_base新增和刪除事件,它可能在select,poll,epoll,kqueue等中執行。
程式碼分析:
這是一個服務端的程式,可以處理客戶端大併發的連線,當收到客戶端的連線後,將收到的資料做了一個變換,如果是 ’a’-‘m’之間的字元,將其增加13,如果是 ’n’-‘z’之間的字元,將其減少13,其他字元不變,然後將轉換後的資料傳送給客戶端。
例如:客戶端傳送:Client 0 send Message!
服務端會回覆:Pyvrag 0 fraq Zrffntr!
在這個程式碼中沒有使用bufferevent這個強大的東西,在一個結構體中自己管理了一個緩衝區。結構體為:
[cpp] view plain copy print?- struct fd_state {
- char buffer[MAX_LINE];//緩衝區的大小
- size_t buffer_used;//接收到已經使用的buffer大小,每次將接收到的資料位元組數相加,當傳送的位元組數累計相加和buffer_used都相等時候,將它們都置為1
- size_t n_written;//傳送的累加位元組數
- size_t write_upto;//相當於一個臨時變數,當遇到換行符的時,將其收到的位元組數(換行符除外)賦給該值,當檢測到寫事件的時候,用已經發送的位元組數和該數值做比較,若收到的位元組總數小於該值,則傳送資料,等於該值,將結構體中3個位元組數統計變數都置為1,為什麼會置為1呢,因為有一個換行符吧。
- struct event *read_event;
- struct event *write_event;
- };
struct fd_state {
char buffer[MAX_LINE];//緩衝區的大小
size_t buffer_used;//接收到已經使用的buffer大小,每次將接收到的資料位元組數相加,當傳送的位元組數累計相加和buffer_used都相等時候,將它們都置為1
size_t n_written;//傳送的累加位元組數
size_t write_upto;//相當於一個臨時變數,當遇到換行符的時,將其收到的位元組數(換行符除外)賦給該值,當檢測到寫事件的時候,用已經發送的位元組數和該數值做比較,若收到的位元組總數小於該值,則傳送資料,等於該值,將結構體中3個位元組數統計變數都置為1,為什麼會置為1呢,因為有一個換行符吧。
struct event *read_event;
struct event *write_event;
};
程式碼中自己管理了一個緩衝區,用於存放接收到的資料,傳送的資料將其轉換後也放入該緩衝區中,程式碼晦澀難懂,我也是經過打日誌分析後,才明白點,這個緩衝區自己還得控制好。但是libevent 2已經提供了一個神器bufferevent,我們在使用的過程中最好不要自己管理這個緩衝區,之所以分析這個程式碼,是為了熟悉libevent 做服務端程式的流程及原理。
下面是程式碼,加有部分註釋和日誌:
程式碼:lowlevel_libevent_server.c
[cpp] view plain copy print?- //說明,為了使我們的程式碼相容win32網路API,我們使用evutil_socket_t代替int,使用evutil_make_socket_nonblocking代替fcntl
- /* For sockaddr_in */
- #include <netinet/in.h>
- /* For socket functions */
- #include <sys/socket.h>
- /* For fcntl */
- #include <fcntl.h>
- #include <event2/event.h>
- #include <assert.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #define MAX_LINE 80
- void do_read(evutil_socket_t fd, short events, void *arg);
- void do_write(evutil_socket_t fd, short events, void *arg);
- char rot13_char(char c)
- {
- /* We don't want to use isalpha here; setting the locale would change
- * which characters are considered alphabetical. */
- if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
- return c + 13;
- elseif ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
- return c - 13;
- else
- return c;
- }
- struct fd_state {
- char buffer[MAX_LINE];
- size_t buffer_used;
- size_t n_written;
- size_t write_upto;
- struct event *read_event;
- struct event *write_event;
- };
- struct fd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd)
- {
- struct fd_state *state = malloc(sizeof(struct fd_state));
- if (!state)
- return NULL;
- state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
- if (!state->read_event)
- {
- free(state);
- return NULL;
- }
- state->write_event = event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
- if (!state->write_event)
- {
- event_free(state->read_event);
- free(state);
- return NULL;
- }
- state->buffer_used = state->n_written = state->write_upto = 0;
- assert(state->write_event);
- return state;
- }
- void free_fd_state(struct fd_state *state)
- {
- event_free(state->read_event);
- event_free(state->write_event);
- free(state);
- }
- void do_read(evutil_socket_t fd, short events, void *arg)
- {
- struct fd_state *state = arg;
- char buf[20];
- int i;
- ssize_t result;
- printf("\ncome in do_read: fd: %d, state->buffer_used: %d, sizeof(state->buffer): %d\n", fd, state->buffer_used, size
- of(state->buffer));
- while (1)
- {
- assert(state->write_event);
- result = recv(fd, buf, sizeof(buf), 0);
- if (result <= 0)
- break;
- printf("recv once, fd: %d, recv size: %d, recv buff: %s\n", fd, result, buf);
- for (i=0; i < result; ++i)
- {
- if (state->buffer_used < sizeof(state->buffer))//如果讀事件的緩衝區還未滿,將收到的資料做轉換
- state->buffer[state->buffer_used++] = rot13_char(buf[i]);
- // state->buffer[state->buffer_used++] = buf[i];//接收什麼傳送什麼,不經過轉換,測試用
- if (buf[i] == '\n') //如果遇到換行,新增寫事件,並設定寫事件的大小
- {
- assert(state->write_event);
- event_add(state->write_event, NULL);
- state->write_upto = state->buffer_used;
- printf("遇到換行符,state->write_upto: %d, state->buffer_used: %d\n",state->write_upto, state->buffer_use
- d);
- }
- }
- printf("recv once, state->buffer_used: %d\n", state->buffer_used);
- }
- //判斷最後一次接收的位元組數
- if (result == 0)
- {
- free_fd_state(state);
- }
- elseif (result < 0)
- {
- if (errno == EAGAIN) // XXXX use evutil macro
- return;
- perror("recv");
- free_fd_state(state);
- }
- }
- void do_write(evutil_socket_t fd, short events, void *arg)
- {
- struct fd_state *state = arg;
- printf("\ncome in do_write, fd: %d, state->n_written: %d, state->write_upto: %d\n",fd, state->n_written, state->write
- _upto);
- while (state->n_written < state->write_upto)
- {
- ssize_t result = send(fd, state->buffer + state->n_written, state->write_upto - state->n_written, 0);
- if (result < 0) {
- if (errno == EAGAIN) // XXX use evutil macro
- return;
- free_fd_state(state);
- return;
- }
- assert(result != 0);
- state->n_written += result;
- printf("send fd: %d, send size: %d, state->n_written: %d\n", fd, result, state->n_written);
- }
- if (state->n_written == state->buffer_used)
- {
- printf("state->n_written == state->buffer_used: %d\n", state->n_written);
- state->n_written = state->write_upto = state->buffer_used = 1;
- printf("state->n_written = state->write_upto = state->buffer_used = 1\n");
- }
- event_del(state->write_event);
- }
- void do_accept(evutil_socket_t listener, short event, void *arg)
- {
- struct event_base *base = arg;
- struct sockaddr_storage ss;
- socklen_t slen = sizeof(ss);
- int fd = accept(listener, (struct sockaddr*)&ss, &slen);
- if (fd < 0)
- { // XXXX eagain??
- perror("accept");
- }
- elseif (fd > FD_SETSIZE)
- {
- close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
- }
- else
- {
- struct fd_state *state;
- evutil_make_socket_nonblocking(fd);
- state = alloc_fd_state(base, fd);
- assert(state); /*XXX err*/
- assert(state->write_event);
- event_add(state->read_event, NULL);
- }
- }
- void run(void)
- {
- evutil_socket_t listener;
- struct sockaddr_in sin;
- struct event_base *base;
- struct event *listener_event;
- base = event_base_new();//初始化libevent
- if (!base)
- return; /*XXXerr*/
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = 0;//本機
- sin.sin_port = htons(8000);
- listener = socket(AF_INET, SOCK_STREAM, 0);
- evutil_make_socket_nonblocking(listener);
- #ifndef WIN32
- {
- int one = 1;
- setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
- }
- #endif
- if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0)
- {
- perror("bind");
- return;
- }
- if (listen(listener, 16)<0)
- {
- perror("listen");
- return;
- }
- listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
- /*XXX check it */
- event_add(listener_event, NULL);
- event_base_dispatch(base);
- }
- int main(int c, char **v)
- {
- // setvbuf(stdout, NULL, _IONBF, 0);
- run();
- return 0;
- }
//說明,為了使我們的程式碼相容win32網路API,我們使用evutil_socket_t代替int,使用evutil_make_socket_nonblocking代替fcntl
/* For sockaddr_in */
#include <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For fcntl */
#include <fcntl.h>
#include <event2/event.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#define MAX_LINE 80
void do_read(evutil_socket_t fd, short events, void *arg);
void do_write(evutil_socket_t fd, short events, void *arg);
char rot13_char(char c)
{
/* We don't want to use isalpha here; setting the locale would change
* which characters are considered alphabetical. */
if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M'))
return c + 13;
else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z'))
return c - 13;
else
return c;
}
struct fd_state {
char buffer[MAX_LINE];
size_t buffer_used;
size_t n_written;
size_t write_upto;
struct event *read_event;
struct event *write_event;
};
struct fd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
struct fd_state *state = malloc(sizeof(struct fd_state));
if (!state)
return NULL;
state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state);
if (!state->read_event)
{
free(state);
return NULL;
}
state->write_event = event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state);
if (!state->write_event)
{
event_free(state->read_event);
free(state);
return NULL;
}
state->buffer_used = state->n_written = state->write_upto = 0;
assert(state->write_event);
return state;
}
void free_fd_state(struct fd_state *state)
{
event_free(state->read_event);
event_free(state->write_event);
free(state);
}
void do_read(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;
char buf[20];
int i;
ssize_t result;
printf("\ncome in do_read: fd: %d, state->buffer_used: %d, sizeof(state->buffer): %d\n", fd, state->buffer_used, size
of(state->buffer));
while (1)
{
assert(state->write_event);
result = recv(fd, buf, sizeof(buf), 0);
if (result <= 0)
break;
printf("recv once, fd: %d, recv size: %d, recv buff: %s\n", fd, result, buf);
for (i=0; i < result; ++i)
{
if (state->buffer_used < sizeof(state->buffer))//如果讀事件的緩衝區還未滿,將收到的資料做轉換
state->buffer[state->buffer_used++] = rot13_char(buf[i]);
// state->buffer[state->buffer_used++] = buf[i];//接收什麼傳送什麼,不經過轉換,測試用
if (buf[i] == '\n') //如果遇到換行,新增寫事件,並設定寫事件的大小
{
assert(state->write_event);
event_add(state->write_event, NULL);
state->write_upto = state->buffer_used;
printf("遇到換行符,state->write_upto: %d, state->buffer_used: %d\n",state->write_upto, state->buffer_use
d);
}
}
printf("recv once, state->buffer_used: %d\n", state->buffer_used);
}
//判斷最後一次接收的位元組數
if (result == 0)
{
free_fd_state(state);
}
else if (result < 0)
{
if (errno == EAGAIN) // XXXX use evutil macro
return;
perror("recv");
free_fd_state(state);
}
}
void do_write(evutil_socket_t fd, short events, void *arg)
{
struct fd_state *state = arg;
printf("\ncome in do_write, fd: %d, state->n_written: %d, state->write_upto: %d\n",fd, state->n_written, state->write
_upto);
while (state->n_written < state->write_upto)
{
ssize_t result = send(fd, state->buffer + state->n_written, state->write_upto - state->n_written, 0);
if (result < 0) {
if (errno == EAGAIN) // XXX use evutil macro
return;
free_fd_state(state);
return;
}
assert(result != 0);
state->n_written += result;
printf("send fd: %d, send size: %d, state->n_written: %d\n", fd, result, state->n_written);
}
if (state->n_written == state->buffer_used)
{
printf("state->n_written == state->buffer_used: %d\n", state->n_written);
state->n_written = state->write_upto = state->buffer_used = 1;
printf("state->n_written = state->write_upto = state->buffer_used = 1\n");
}
event_del(state->write_event);
}
void do_accept(evutil_socket_t listener, short event, void *arg)
{
struct event_base *base = arg;
struct sockaddr_storage ss;
socklen_t slen = sizeof(ss);
int fd = accept(listener, (struct sockaddr*)&ss, &slen);
if (fd < 0)
{ // XXXX eagain??
perror("accept");
}
else if (fd > FD_SETSIZE)
{
close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */
}
else
{
struct fd_state *state;
evutil_make_socket_nonblocking(fd);
state = alloc_fd_state(base, fd);
assert(state); /*XXX err*/
assert(state->write_event);
event_add(state->read_event, NULL);
}
}
void run(void)
{
evutil_socket_t listener;
struct sockaddr_in sin;
struct event_base *base;
struct event *listener_event;
base = event_base_new();//初始化libevent
if (!base)
return; /*XXXerr*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;//本機
sin.sin_port = htons(8000);
listener = socket(AF_INET, SOCK_STREAM, 0);
evutil_make_socket_nonblocking(listener);
#ifndef WIN32
{
int one = 1;
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
}
#endif
if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0)
{
perror("bind");
return;
}
if (listen(listener, 16)<0)
{
perror("listen");
return;
}
listener_event = event_new(base, listener, EV_READ|EV_PERSIST, do_accept, (void*)base);
/*XXX check it */
event_add(listener_event, NULL);
event_base_dispatch(base);
}
int main(int c, char **v)
{
// setvbuf(stdout, NULL, _IONBF, 0);
run();
return 0;
}
編譯:gcc -I/usr/include -o test lowlevel_libevent_server.c -L/usr/local/lib -levent
執行結果:
相關推薦
處理大併發之四 libevent demo詳細分析(對比epoll)
處理大併發之四 libevent demo詳細分析(對比epoll) libevent預設情況下是單執行緒,每個執行緒有且僅有一個event_base,對應一個struct event_base結構體,以及賦予其上的事件管理器,用來安排託管給它的一系列的事件。 當有一個事件
處理大併發之 libevent demo詳細分析(對比epoll)
libevent預設情況下是單執行緒,每個執行緒有且僅有一個event_base,對應一個struct event_base結構體,以及賦予其上的事件管理器,用來安排託管給它的一系列的事件。 當有一個事件發生的時候,event_base會在合適的時間去呼叫繫結在
程式設計菜鳥到大佬之路:C語言程式(十二)
第十二天學習精要 遞迴初步 遞迴 一個函式,自己呼叫自己,就是遞迴。 # include <iostream> using namespace std; int factorial(int n) // 函式返回n的階乘 { if (n ==
c/c++常用程式碼之四爆炸輸出,jason(純乾貨)
常用程式碼之四:建立jason,jason轉換為字串,字串轉換回jason,c#反序列化jason字串的幾個程式碼片段 建立j
LinkedHashMap 原始碼詳細分析(JDK1.8)
1. 概述 LinkedHashMap 繼承自 HashMap,在 HashMap 基礎上,通過維護一條雙向連結串列,解決了 HashMap 不能隨時保持遍歷順序和插入順序一致的問題。除此之外,LinkedHashMap 對訪問順序也提供了相關支援。在一些場景下,該特性很有用,比如快取。在實現上
hostapd wpa_supplicant madwifi詳細分析(十二)——EAP(RFC3748)及EAP狀態機分析(RFC4137)
這篇文章分兩個部分:EAP(RFC3748)及EAP狀態機分析(RFC4137),其中主要內容來自RFC以及網路文章。 一、EAP拓展認證協議 EAP的可拓展性主要表現在它的method可拓展,EAP只是一個載體,傳送不同method間的互動。 EAP可用於專用的連結,以
lk啟動流程詳細分析(高通)
int boot_linux_from_mmc(void) { struct boot_img_hdr *hdr = (void*) buf; //************buf和hdr指向相同的地址,可以理解為buf
C++11併發學習之四:執行緒同步(續)
有時候,在第一個執行緒完成前,可能需要等待另一個執行緒執行完成。C++標準庫提供了一些工具可用於這種同步操作,形式上表現為條件變數(condition variable)和期望(future)。 一.條件變數(condition variable) C++標準庫對條件變數有兩套實現:std::c
java大併發之資料庫層面解決方案
前言:當一個網站開始剛剛建立時,可能只是考慮一天只有幾十或者幾百個人訪問,資料庫可能就個db,所有表都放一起,一臺普通的伺服器可能就夠了,而且開發人員也非常高興,而且信心十足,因為所有的表都在一個庫中,這樣查詢語句就可以隨便關聯了,多美的一件事情。但是隨著訪問壓力的增加,
大資料之四 hadoop HDFS HA 高可用的完全分散式
HDFS HA HA即為High Availability,用於解決NameNode單點故障問題,該特性通過熱備的方式為主NameNode提供一個備用者,一旦主NameNode出現故障,可以迅速切換至備NameNode,從而實現對外提供更穩定的服務 Second
小談php處理 大併發 大流量 大儲存
一、判斷大型網站的標準: 1.pv(page views)網頁的瀏覽量: 概念:一個網站所有的頁面,在24小時內被訪問的總的次數。千萬級別,百萬級別, 2、uv值(unique vistor)獨立訪客 概念:一個網站,在24小時內,有多少個使用者來訪問我
Goroutine(協程)為何能處理大併發
簡單來說:協程十分輕量,可以在一個程序中執行有數以十萬計的協程,依舊保持高效能。 程序、執行緒、協程的關係和區別: 程序擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,程序由作業系統排程。 執行緒擁有自己獨立的棧和共享的堆,共享堆,不共享棧,執行緒亦由作業系統排程(標準執行緒是的)。 協程和執行緒一
處理大併發之一 對epoll的理解,epoll客戶端服務端程式碼
處理大併發之一 對epoll的理解,epoll客戶端服務端程式碼 序言: 該部落格是一系列的部落格,首先從最基礎的epoll說起,然後研究libevent原始碼及使用方法,最後研究nginx和node.js,關於select,poll這裡不做說明,只說明其相對於epoll的
python 處理大資料之資料讀取
1 參考1:python讀取GB級的文字資料,防止memoryError Preliminary 我們談到“文字處理”時,我們通常是指處理的內容。Python 將文字檔案的內容讀入可以操作的字串變數非常容易。檔案物件提供了三個“讀”方法: .read()、.read
Flink處理函式實戰之四:視窗處理
### 歡迎訪問我的GitHub [https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) 內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等; ### F
MySql之ALTER命令用法詳細解讀(轉)
修改表 pre const 命令使用 add ear 修改 blog rain 本文詳細解讀了MySql語法中Alter命令的用法,這是一個用法比較多的語法,而且功能還是很強大的。 USE learning;(自己要提前建好) CREATE TABLE student
Flutter之drawer詳細分析(你要的操作都有)
1. 簡介 這篇文章主要講解有關drawer的一切。 另:接Flutter相關專案,需要的私信或通過QQ:708959817,聯絡我 2. 初探 我們先來看看簡單的drawer在Flutter的應用 class HomePage extends StatefulWidget {
1.大資料指CDH叢集搭建詳細步驟(一)
1.使用CDH,其中CDH表示的意思是Cloudera’s Distribution Including Apache Hadoop,簡稱“CDH”) 基於web的使用者介面,支援大多數的hadoop元件,包括了HDFS,MapReduce以及HIve和Pig Hbase以及Zookeepe
ccf歷年第四題java解答之-201503-4-網路延時(90分)
使用bfs求樹的直徑,執行超時,90分 import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; class Node{ public int no; public int
ccf歷年第四題java解答之-201412-4-最優灌溉(100分)
使用kruskal求解,耗時943ms,得分100 徘徊在超時的邊緣,同樣的程式碼,有時候提交是100分,有時候是超時90分,還有時候是超時80分== import java.util.ArrayList; import java.util.Collections; import jav