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

Conversation

yanglimingcn
Copy link
Contributor

Add Mysql Protocol, support text protocol, transaction, support prepare statement.

issue #209

What problem does this PR solve?

Issue Number:

Problem Summary:

What is changed and the side effects?

Changed:

Side effects:

  • Performance effects(性能影响):

  • Breaking backward compatibility(向后兼容性):


Check List:

  • Please make sure your changes are compilable(请确保你的更改可以通过编译).
  • When providing us with a new feature, it is best to add related tests(如果你向我们增加一个新的功能, 请添加相关测试).
  • Please follow Contributor Covenant Code of Conduct.(请遵循贡献者准则).

@yanglimingcn
Copy link
Contributor Author

mysql的prepared statement协议的支持。
遇到的两个问题解决方案如下:
1、prepared statement首先是在某个连接上创建一个statement id,后续操作可以根据这个statement id向这个连接发送后续的请求。同一个语句在每个连接上的statement id是不同的。在pooled模式下,每次RPC选择的连接是不同的,所以需要记录SocketId和statement id的关系,我们这里称为id_map。但是一个SocketId有可能关闭,然后又重新建立起来,这个新SocketId对应的连接没有这个statement id。id_map没有机制可以做到同步更新,保存的SocketId与statement id关系信息是旧的关系。这样就会产生问题。
这个问题的修改方式是,给socketid对应的fd一个唯一的版本号,每次通过stmt_id找到socketid的时候对比一下版本号,版本号一致说明fd没有变化,版本号不一致说明fd变化过。
2、目前还不能支持single模式,唯一的原因就是auth部分,正常auth多个请求会竞争发送auth,后续操作就不需要发送auth了,auth不用等待服务端应答,就可以让其他请求继续发送。但是mysql的auth和这个逻辑不通,mysql的auth首先接收mysql-server发送过来的salt,客户端根据salt加密密码,再把加密后的密码发送给服务端。这样的逻辑如果不等待服务端应答就解锁别的请求,就会出现发送auth的请求落后于正常的请求,导致消息乱序。
这个目前就先不支持single模式了。

@@ -0,0 +1,562 @@
[MySQL](https://www.mysql.com/)是著名的开源的关系型数据库,为了使用户更快捷地访问mysql并充分利用bthread的并发能力,brpc直接支持mysql协议。示例程序:[example/mysql_c++](https://github.com/brpc/brpc/tree/master/example/mysql_c++/)

**注意**:只支持MySQL 4.1 及之后的版本的文本协议,支持事务,不支持Prepared statement。目前支持的鉴权方式为mysql_native_password,使用事务的时候不支持single模式。
Copy link
Contributor

Choose a reason for hiding this comment

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

现在是不是支持Prepared statement了


- 请求类型必须为MysqlRequest,回复类型必须为MysqlResponse,否则CallMethod会失败。不需要stub,直接调用channel.CallMethod,method填NULL。
- 调用request.Query()传入要执行的命令,可以批量执行命令,多个命令用分号隔开。
- 依次调用response.reply(X)弹出操作结果,根据返回类型的不同,选择不同的类型接收,如:MysqlReply::Ok,MysqlReply::Error,const MysqlReply::Columnconst MysqlReply::Row等。
Copy link
Contributor

Choose a reason for hiding this comment

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

Columnconst 中间少了个逗号

事务可以保证在一个事务中的多个RPC请求最终要么都成功,要么都失败。

```c++
rpc::Channel channel;
Copy link
Contributor

Choose a reason for hiding this comment

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

这一段初始化Channel的代码可以略去


##### libmysqlclient实现(100线程)

./mysql_press -thread_num=50 -op_type=1 -use_bthread=true
Copy link
Contributor

Choose a reason for hiding this comment

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

50应改成100

@@ -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

}
const char* delim1 = "&";
std::vector<size_t> idx;
for (size_t p = params.find(delim1); p != butil::StringPiece::npos;
Copy link
Contributor

Choose a reason for hiding this comment

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

可以使用butil::StringSplitter或butil::SplitString


DEFINE_bool(mysql_verbose, false, "[DEBUG] Print EVERY mysql request/response");

void MysqlParseAuthenticator(const butil::StringPiece& raw,
Copy link
Contributor

Choose a reason for hiding this comment

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

这几个函数是不是可以在mysql_authenticator.h里声明一下

return true;
}

void MysqlParseAuthenticator(const butil::StringPiece& raw,
Copy link
Contributor

Choose a reason for hiding this comment

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

是不是可以实现成MysqlAuthenticator::ParseFromString 这样更对称一些

std::string mysql_build_mysql41_authentication_response(const std::string& salt_data,
const std::string& password);

std::string mysql_build_sha256_authentication_response(const std::string& salt_data,
Copy link
Contributor

Choose a reason for hiding this comment

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

这个哪里用到了

_type = MYSQL_RSP_UNKNOWN;
_data.padding = 0;
}
inline void MysqlReply::Swap(MysqlReply& other) {
Copy link
Contributor

Choose a reason for hiding this comment

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

加个空行,下同

return rc;
}

ParseError MysqlReply::Field::ParseBinaryDataTime(butil::IOBuf& buf,
Copy link
Contributor

Choose a reason for hiding this comment

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

DataTime -> DateTime

MYSQL_SET_FLAG = 0x0800,
};

enum MysqlServerStatus : uint16_t {
Copy link
Contributor

Choose a reason for hiding this comment

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

这个哪里用了

namespace brpc {

namespace {
const uint32_t max_allowed_packet = 67108864;
Copy link
Contributor

Choose a reason for hiding this comment

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

用全大写命名吧

if (seq == 0) {
const uint32_t old_size = outbuf->size();
outbuf->append(head);
size -= outbuf->size() - old_size;
Copy link
Contributor

Choose a reason for hiding this comment

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

为什么不直接用size -= head.size()呢


} // namespace

butil::Status MysqlMakeCommand(butil::IOBuf* outbuf,
Copy link
Contributor

Choose a reason for hiding this comment

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

这个叫MysqlMakeCommandPacket会不会更合适

typedef butil::FlatMap<SocketId, MysqlStatementId> MysqlStatementKVMap;
typedef butil::DoublyBufferedData<MysqlStatementKVMap> MysqlStatementDBD;

inline size_t my_init_kv(MysqlStatementKVMap& m) {
Copy link
Contributor

Choose a reason for hiding this comment

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

这个不用放在头文件里吧,放在mysql_statement.cpp里就行了

// LOG(INFO) << response.reply(0);
// }

class MysqlStatementStub {
Copy link
Contributor

Choose a reason for hiding this comment

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

这个类是不是放在mysql_statment.h里更合适

return MakePacket(outbuf, head, func, edata);
}

butil::Status MysqlMakeExecuteData(MysqlStatementStub* stmt,
Copy link
Contributor

Choose a reason for hiding this comment

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

这个作为MysqlStatementStub的成员函数会不会更合适

@@ -104,6 +104,14 @@ enum StopStyle {

const int32_t UNSET_MAGIC_NUM = -123456789;

// if controller want to reserve a sock after RPC, set BIND_SOCK_ACTIVE
Copy link
Contributor

Choose a reason for hiding this comment

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

这个ACTIVE不是很好理解,是不是可以叫BIND_SOCK_RESERVE

Comment on lines +1038 to +1040
if (!tmp_sock || (!is_health_check_call() && !tmp_sock->IsAvailable())) {
SetFailed(EHOSTDOWN, "Not connected to bind socket yet, server_id=%" PRIu64,
tmp_sock->id());
Copy link
Contributor

Choose a reason for hiding this comment

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

tmp_sock为NULL,tmp_sock->id()会coredump吧?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants