12
12
@li ... and much more
14
Internally the MySQL Proxy is a stack of:
16
@dotfile architecture-overview.dot
18
It is based on a @subpage page-core that exposes the phases of the
19
@subpage protocol to a @ref page-plugins.
25
command -> disconnect;
27
connect -> disconnect;
33
Each of the phases of the life-cycle lead to several more protocol-states. For example the auth phase is made up of at least 3 packets:
36
Client, Proxy, Server;
38
Client -> Proxy [ label = "accept()" ];
39
Proxy -> Proxy [ label = "script: connect_server()" ];
40
Proxy -> Server [ label = "connect()" ];
42
Server -> Proxy [ label = "recv(auth-challenge)" ];
43
Proxy -> Proxy [ label = "script: read_handshake()" ];
44
Proxy -> Client [ label = "send(auth-challenge)" ];
45
Client -> Proxy [ label = "recv(auth-response)" ];
46
Proxy -> Proxy [ label = "script: read_auth()" ];
47
Server -> Proxy [ label = "send(auth-response)" ];
48
Server -> Proxy [ label = "recv(auth-result)" ];
49
Proxy -> Proxy [ label = "script: read_auth_result()" ];
50
Proxy -> Client [ label = "send(auth-result)" ];
55
While the @ref page-core is scalable to a larger number of connections, the plugin/scripting
56
layer hides the complexity from the end-users and simplifies the customization.
58
@section section-stack-of-libs Chassis, libraries and Plugins
60
It is built as a stack of libraries:
62
The @subpage page-chassis provides the common functions that all commandline and daemon applications
64
@li commandline and configfiles
66
@li daemon/service support
69
The MySQL Procotol libraries which can encode and decode:
76
The @ref page-core and the @subpage page-plugins.
78
@dotfile architecture.dot
14
It is built of different components that which are stacked and can be used independently:
17
* commandline and configfiles
19
* daemon/service support
21
@li @ref protocol libraries
29
* connection life-cycle
32
@page page.chassis Chassis
34
@section section-chassisconfigfile Configfile and Commandline Options
36
GLib2 provides us with config-file parser and a command-line option parser. We want to expose
37
most options in the same way and accept them from the config-file and the command-line.
39
The options are parsed in two steps:
41
@li extract the basic command-line options:
45
@li process the defaults-file
46
@li process the other command-line options to override the defaults-file
48
@see src/chassis-keyfile.h and src/chassic.c
50
@section section-chassisplugin Plugin Interface
52
The chassis provides the fundamentals for the plugin interface:
54
@li it can resolve the path for plugins
55
@li can load them in a portable way
56
@li does version checks
57
@li calls init and shutdown functions
58
@li exposes the configuration options to the plugins
60
@see src/chassis-plugin.h and struct chassis_plugin
62
As the chassis is not MySQL specific it can load any kind of plugin as long as it
63
exposes the init and shutdown functions.
65
For the MySQL Proxy you usually load plugins like:
70
... which interface with MySQL @ref section-lifecycle (see below).
72
@page page.core Network Core
74
The core exposes Connection Life Cycle to the plugins through callbacks. The plugins can
75
decide how get from one state to the next.
77
@section section-lifecycle Connection Life Cycle
79
The MySQL Proxy protocol has 4 phases:
88
The lifecycle image shows the default implementation of the proxy plugin. It implements all
89
the callbacks and exposes them to its scripting layer. It implements the listening and the
90
connecting side and forwards packets between them
92
Other plugins may implement only one side of the callbacks:
93
@li admin plugin implements only the listening side
94
@li client plugins implement only the connection side
96
@subsection section-scripting Scripting
98
Most plugins implement a set of those callbacks and expose them to a scripting layer.
100
For now the scriping is provided by Lua, a simple, fast and easy to embed scripting language.
101
We expose most of the internals into the scripting layer and provide modules to operate
102
on the data that we pass into the scripting layer
104
@section section-network-core-layer Network Core Layer
106
The MySQL Proxy network engine is meant to handle several thousands connections at the same time. We
107
want to use it for load-balancing and fail-over which means we have to handle the connections for
108
a larger group of MySQL backend servers nicely. We aim for 5k to 10k connections.
109
Up to MySQL Proxy 0.7 we use a pure event-driven, non-blocking networking approach is described in
110
http://kegel.com/c10k.html#nb using libevent 1.4.x.
111
A event-driven design has a very small foot-print for idling connections: we just store the
112
connection state and let it wait for a event.
114
@section section-threaded-io Threaded IO
116
In MySQL 0.8 we added threaded network-io to allow the proxy to scale out with the numbers of CPUs
117
and network cards available.
119
To enable network-threading you just start the proxy with:
122
--event-threads={2 * no-of-cores} (default: 0)
125
A event-thread is a simple small thread around "event_base_dispatch()" which on a network- or time-event
126
executes our core functions. These threads either execute the core functions or idle. If they idle
127
they can read new events to wait for and add them to their wait-list.
129
A connection can jump between event-threads: the idling event-thread is taking the wait-for-event
130
request and executes the code. Whenever the connection has to wait for a event again it is unregister
131
itself from the thread, send its wait-for-event request to the global event-queue again.
133
Up to MySQL Proxy 0.8 the execution of the scripting code is single-threaded: a global mutex protects
134
the plugin interface. As a connection is either sending packets or calling plugin function the network
135
events will be handled in parallel and only wait if several connections want to call a plugin function
137
@subsection section-threaded-io-impl Implementation
139
In chassis-event-thread.c the chassis_event_thread_loop() is the event-thread itself. It gets setup by
140
chassis_event_threads_init_thread() which registers itself to a pipe(), a classic simple pipe. That
141
pipe is a common hack in libevent to map any kind of event to a the fd-based event-handlers like poll:
143
@li the @c event_base_dispatch() blocks until a fd-event triggers
144
@li timers, signals, ... can't interrupt @c event_base_dispatch() directly
145
@li instead they cause a @c write(pipe_fd, ".", 1); which triggers a fd-event
146
which afterwards gets handled
148
In chassis-event-thread.c we use the pipe to signal that something is in the global event-queue to be
149
processed by one of the event-threads ... see chassis_event_handle(). All idling threads will process
150
that even and will pull from the event queue in parallel to add the event to their events to listen for.
152
To add a event to the event-queue you can call chassis_event_add() or chassis_event_add_local(). See
153
the source when to use which.
155
@subsection section-threaded-scripting Threaded Scripting
157
Usually the scripts are small and only make simple decisions leaving most of the work to the network layer.
158
In 0.9 we will make the scripting layer multi-threaded allow several scripting threads at the same time,
159
working from a small pool threads.
161
That will allow the scripting layer to call blocking or slow functions without infecting the execution of
164
Lifting the global plugin mutex will mean we have to handle access to global structure differently. Most
165
of the access is happening on connection-level (the way we do the event-threading) and only access to
166
global structures like "proxy.global.*" has to synchronized. For that we will look into using Lua lanes
167
to send data around between independent Lua-states.