Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Mysql Protocol #2093

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
562 changes: 562 additions & 0 deletions docs/cn/mysql_client.md

Large diffs are not rendered by default.

Binary file added docs/images/mysql_memory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/mysql_select.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/mysqlclient_select.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 148 additions & 0 deletions example/mysql_c++/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
cmake_minimum_required(VERSION 2.8.10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

加上license

project(mysql_c++ C CXX)

# Install dependencies:
# With apt:
# sudo apt-get install libreadline-dev
# sudo apt-get install ncurses-dev
# With yum:
# sudo yum install readline-devel
# sudo yum install ncurses-devel

option(EXAMPLE_LINK_SO "Whether examples are linked dynamically" OFF)

execute_process(
COMMAND bash -c "find ${PROJECT_SOURCE_DIR}/../.. -type d -regex \".*output/include$\" | head -n1 | xargs dirname | tr -d '\n'"
OUTPUT_VARIABLE OUTPUT_PATH
)

set(CMAKE_PREFIX_PATH ${OUTPUT_PATH})

include(FindThreads)
include(FindProtobuf)

# Search for libthrift* by best effort. If it is not found and brpc is
# compiled with thrift protocol enabled, a link error would be reported.
find_library(THRIFT_LIB NAMES thrift)
if (NOT THRIFT_LIB)
set(THRIFT_LIB "")
endif()
find_library(THRIFTNB_LIB NAMES thriftnb)
if (NOT THRIFTNB_LIB)
set(THRIFTNB_LIB "")
endif()

find_path(BRPC_INCLUDE_PATH NAMES brpc/server.h)
if(EXAMPLE_LINK_SO)
find_library(BRPC_LIB NAMES brpc)
else()
find_library(BRPC_LIB NAMES libbrpc.a brpc)
endif()
if((NOT BRPC_INCLUDE_PATH) OR (NOT BRPC_LIB))
message(FATAL_ERROR "Fail to find brpc")
endif()
include_directories(${BRPC_INCLUDE_PATH})

find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
if((NOT GFLAGS_INCLUDE_PATH) OR (NOT GFLAGS_LIBRARY))
message(FATAL_ERROR "Fail to find gflags")
endif()
include_directories(${GFLAGS_INCLUDE_PATH})

execute_process(
COMMAND bash -c "grep \"namespace [_A-Za-z0-9]\\+ {\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $2}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)
if(${GFLAGS_NS} STREQUAL "GFLAGS_NAMESPACE")
execute_process(
COMMAND bash -c "grep \"#define GFLAGS_NAMESPACE [_A-Za-z0-9]\\+\" ${GFLAGS_INCLUDE_PATH}/gflags/gflags_declare.h | head -1 | awk '{print $3}' | tr -d '\n'"
OUTPUT_VARIABLE GFLAGS_NS
)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
include(CheckFunctionExists)
CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME)
if(NOT HAVE_CLOCK_GETTIME)
set(DEFINE_CLOCK_GETTIME "-DNO_CLOCK_GETTIME_IN_MAC")
endif()
endif()

set(CMAKE_CPP_FLAGS "${DEFINE_CLOCK_GETTIME} -DGFLAGS_NS=${GFLAGS_NS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CPP_FLAGS} -DNDEBUG -O2 -D__const__= -pipe -W -Wall -Wno-unused-parameter -fPIC -fno-omit-frame-pointer")

if(CMAKE_VERSION VERSION_LESS "3.1.3")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
else()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()

find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
find_library(LEVELDB_LIB NAMES leveldb)
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
message(FATAL_ERROR "Fail to find leveldb")
endif()
include_directories(${LEVELDB_INCLUDE_PATH})

find_library(SSL_LIB NAMES ssl)
if (NOT SSL_LIB)
message(FATAL_ERROR "Fail to find ssl")
endif()

find_library(CRYPTO_LIB NAMES crypto)
if (NOT CRYPTO_LIB)
message(FATAL_ERROR "Fail to find crypto")
endif()

# find_path(MYSQL_INCLUDE_PATH NAMES mysql/mysql.h)
# find_library(MYSQL_LIB NAMES mysqlclient)
# if (NOT MYSQL_LIB)
# message(FATAL_ERROR "Fail to find mysqlclient")
# endif()
# include_directories(${MYSQL_INCLUDE_PATH})

set(DYNAMIC_LIB
${CMAKE_THREAD_LIBS_INIT}
${GFLAGS_LIBRARY}
${PROTOBUF_LIBRARIES}
${LEVELDB_LIB}
${SSL_LIB}
${CRYPTO_LIB}
${THRIFT_LIB}
${THRIFTNB_LIB}
# ${MYSQL_LIB}
dl
)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(DYNAMIC_LIB ${DYNAMIC_LIB}
pthread
"-framework CoreFoundation"
"-framework CoreGraphics"
"-framework CoreData"
"-framework CoreText"
"-framework Security"
"-framework Foundation"
"-Wl,-U,_MallocExtension_ReleaseFreeMemory"
"-Wl,-U,_ProfilerStart"
"-Wl,-U,_ProfilerStop")
endif()

add_executable(mysql_cli mysql_cli.cpp)
add_executable(mysql_tx mysql_tx.cpp)
add_executable(mysql_stmt mysql_stmt.cpp)
add_executable(mysql_press mysql_press.cpp)
# add_executable(mysqlclient_press mysqlclient_press.cpp)

set(AUX_LIB readline ncurses)
target_link_libraries(mysql_cli ${BRPC_LIB} ${DYNAMIC_LIB} ${AUX_LIB})
target_link_libraries(mysql_tx ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(mysql_stmt ${BRPC_LIB} ${DYNAMIC_LIB})
target_link_libraries(mysql_press ${BRPC_LIB} ${DYNAMIC_LIB})
# target_link_libraries(mysqlclient_press ${BRPC_LIB} ${DYNAMIC_LIB})
168 changes: 168 additions & 0 deletions example/mysql_c++/mysql_cli.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright (c) 2014 Baidu, Inc.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

要改成Apache License header,下同

//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A brpc based command-line interface to talk with mysql-server

#include <signal.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <gflags/gflags.h>
#include <butil/logging.h>
#include <brpc/channel.h>
#include <brpc/mysql.h>
#include <brpc/policy/mysql_authenticator.h>

DEFINE_string(connection_type, "pooled", "Connection type. Available values: pooled, short");
DEFINE_string(server, "127.0.0.1", "IP Address of server");
DEFINE_int32(port, 3306, "Port of server");
DEFINE_string(user, "brpcuser", "user name");
DEFINE_string(password, "12345678", "password");
DEFINE_string(schema, "brpc_test", "schema");
DEFINE_string(params, "", "params");
DEFINE_string(collation, "utf8mb4_general_ci", "collation");
DEFINE_int32(timeout_ms, 5000, "RPC timeout in milliseconds");
DEFINE_int32(connect_timeout_ms, 5000, "RPC timeout in milliseconds");
DEFINE_int32(max_retry, 0, "Max retries(not including the first RPC)");

namespace brpc {
const char* logo();
}

// Send `command' to mysql-server via `channel'
static bool access_mysql(brpc::Channel& channel, const char* command) {
brpc::MysqlRequest request;
if (!request.Query(command)) {
LOG(ERROR) << "Fail to add command";
return false;
}
brpc::MysqlResponse response;
brpc::Controller cntl;
channel.CallMethod(NULL, &cntl, &request, &response, NULL);
if (!cntl.Failed()) {
std::cout << response << std::endl;
} else {
LOG(ERROR) << "Fail to access mysql, " << cntl.ErrorText();
return false;
}
return true;
}

// For freeing the memory returned by readline().
struct Freer {
void operator()(char* mem) {
free(mem);
}
};

static void dummy_handler(int) {}

// The getc for readline. The default getc retries reading when meeting
// EINTR, which is not what we want.
static bool g_canceled = false;
static int cli_getc(FILE* stream) {
int c = getc(stream);
if (c == EOF && errno == EINTR) {
g_canceled = true;
return '\n';
}
return c;
}

int main(int argc, char* argv[]) {
// Parse gflags. We recommend you to use gflags as well.
GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true);

// A Channel represents a communication line to a Server. Notice that
// Channel is thread-safe and can be shared by all threads in your program.
brpc::Channel channel;

// Initialize the channel, NULL means using default options.
brpc::ChannelOptions options;
options.protocol = brpc::PROTOCOL_MYSQL;
options.connection_type = FLAGS_connection_type;
options.timeout_ms = FLAGS_timeout_ms /*milliseconds*/;
options.connect_timeout_ms = FLAGS_connect_timeout_ms;
options.max_retry = FLAGS_max_retry;
options.auth = new brpc::policy::MysqlAuthenticator(
FLAGS_user, FLAGS_password, FLAGS_schema, FLAGS_params, FLAGS_collation);
if (channel.Init(FLAGS_server.c_str(), FLAGS_port, &options) != 0) {
LOG(ERROR) << "Fail to initialize channel";
return -1;
}

if (argc <= 1) { // interactive mode
// We need this dummy signal hander to interrupt getc (and returning
// EINTR), SIG_IGN did not work.
signal(SIGINT, dummy_handler);

// Hook getc of readline.
rl_getc_function = cli_getc;

// Print welcome information.
printf("%s\n", brpc::logo());
printf(
"This command-line tool mimics the look-n-feel of official "
"mysql-cli, as a demostration of brpc's capability of"
" talking to mysql-server. The output and behavior is "
"not exactly same with the official one.\n\n");

for (;;) {
char prompt[128];
snprintf(prompt, sizeof(prompt), "mysql %s> ", FLAGS_server.c_str());
std::unique_ptr<char, Freer> command(readline(prompt));
if (command == NULL || *command == '\0') {
if (g_canceled) {
// No input after the prompt and user pressed Ctrl-C,
// quit the CLI.
return 0;
}
// User entered an empty command by just pressing Enter.
continue;
}
if (g_canceled) {
// User entered sth. and pressed Ctrl-C, start a new prompt.
g_canceled = false;
continue;
}
// Add user's command to history so that it's browse-able by
// UP-key and search-able by Ctrl-R.
add_history(command.get());

if (!strcmp(command.get(), "help")) {
printf("This is a mysql CLI written in brpc.\n");
continue;
}
if (!strcmp(command.get(), "quit")) {
// Although quit is a valid mysql command, it does not make
// too much sense to run it in this CLI, just quit.
return 0;
}
access_mysql(channel, command.get());
}
} else {
std::string command;
command.reserve(argc * 16);
for (int i = 1; i < argc; ++i) {
if (i != 1) {
command.push_back(';');
}
command.append(argv[i]);
}
if (!access_mysql(channel, command.c_str())) {
return -1;
}
}
return 0;
}
63 changes: 63 additions & 0 deletions example/mysql_c++/mysql_go_press.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

加上license header


import (
"database/sql"
"flag"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"sync/atomic"
"time"
)

var thread_num int

func init() {
flag.IntVar(&thread_num, "thread_num", 1, "thread number")
}

var cost int64
var qps int64 = 1

func main() {
flag.Parse()

db, err := sql.Open("mysql", "brpcuser:12345678@tcp(127.0.0.1:3306)/brpc_test?charset=utf8")
if err != nil {
log.Fatal(err)
}

for i := 0; i < thread_num; i++ {
go func() {
for {
var (
id int
col1 string
col2 string
col3 string
col4 string
)
start := time.Now()
rows, err := db.Query("select * from brpc_press where id = 1")
if err != nil {
log.Fatal(err)
}
for rows.Next() {
if err := rows.Scan(&id, &col1, &col2, &col3, &col4); err != nil {
log.Fatal(err)
}
}
atomic.AddInt64(&cost, time.Since(start).Nanoseconds())
atomic.AddInt64(&qps, 1)
}
}()
}

var q int64 = 0
for {
fmt.Println("qps =", qps-q, "latency =", cost/(qps-q)/1000)
q = atomic.LoadInt64(&qps)
atomic.StoreInt64(&cost, 0)
time.Sleep(1 * time.Second)
}
}
Loading