Mycat as a small knowledge point of proxy server

Posted by htcilt on Thu, 13 Jan 2022 07:18:11 +0100

I preface

Mycat exposes services in the form of a Server, and its main configuration class is Server XML, this article mainly focuses on some small details, mainly including:

  • Server. How to load XML
  • Configuration and role in Server
  • Flow of Server in connection request

II Server.xml configuration

2.1 Server.xml configuration details

Server.xml is mainly loaded in XML schemaloader, server The XML file is mainly divided into the following parts

User management

<user name="user">
    <property name="password">user</property>
    // User accessible schemas can be separated by commas
    <property name="schemas">db001</property>
    <property name="readOnly">true</property>
    <property name="defaultSchema">db001</property>
</user>
Copy code

configuration management

The configuration file is pulled directly from the source code, which is also very clear. If you are interested, you can pull the source code to have a look

<system>
       <property name="nonePasswordLogin">0</property> <!-- 0 For login with password, 1 for login without password ,The default is 0. If it is set to 1, you need to specify the default account-->
       <property name="ignoreUnknownCommand">0</property><!-- 0 An unrealized message is encountered(Unknown command:),An error will be reported. 1 means to ignore the message and return ok Message.
In some mysql When the client has logged in, it will continue to send login messages,mycat Will report an error,This setting can bypass this error-->
       <property name="useHandshakeV10">1</property>
       <property name="removeGraveAccent">1</property>
       <property name="useSqlStat">0</property>  <!-- 1 To enable real-time statistics, 0 to disable -->
       <property name="useGlobleTableCheck">0</property>  <!-- 1 To enable all overtime consistency detection, and 0 to close -->
       <property name="sqlExecuteTimeout">300</property>  <!-- SQL Execution timeout unit:second-->
       <property name="sequenceHandlerType">1</property>
       <!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|)|\s)*)+</property>
       INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx");
       -->
       <!--Must have MYCATSEQ_perhaps mycatseq_Enter the sequence matching process. Note: MYCATSEQ_With spaces-->
       <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|)|\s)*)+</property>
       <property name="subqueryRelationshipCheck">false</property> <!-- When there is an associated query in the sub query,Check whether there is a fragment field in the associated field .default false -->
       <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property>
       <!--  <property name="useCompression">1</property>--> <!--1 For on mysql Compression protocol-->
       <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--Set simulated MySQL Version number-->
       <!-- <property name="processorBufferChunk">40960</property> -->
       <!--
       <property name="processors">1</property>
       <property name="processorExecutor">32</property>
        -->
       <!--Default to type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool -->
       <property name="processorBufferPoolType">0</property>
       <!--The default is 65535 64 K be used for sql Maximum text length when parsing -->
       <!--<property name="maxStringLiteralLength">65535</property>-->
       <!--<property name="sequenceHandlerType">0</property>-->
       <!--<property name="backSocketNoDelay">1</property>-->
       <!--<property name="frontSocketNoDelay">1</property>-->
       <!--<property name="processorExecutor">16</property>-->
       <!--
           <property name="serverPort">8066</property> <property name="managerPort">9066</property>
           <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property>
           <property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //Connection idle check
           <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
       <!--Distributed transaction switch: 0 means not filtering distributed transactions, 1 means filtering distributed transactions (if only global tables are involved in distributed transactions, then not filtering), and 2 means not filtering distributed transactions,However, distributed transaction logs are recorded-->
       <property name="handleDistributedTransactions">0</property>

       <!--
       off heap for merge/order/group/limit      1 On 0 off
   -->
       <property name="useOffHeapForMerge">0</property>

       <!--
           Unit is m
       -->
       <property name="memoryPageSize">64k</property>

       <!--
           Unit is k
       -->
       <property name="spillsFileBufferSize">1k</property>

       <property name="useStreamOutput">0</property>

       <!--
           Unit is m
       -->
       <property name="systemReserveMemorySize">384m</property>


       <!--Whether to adopt zookeeper Coordinated switching  -->
       <property name="useZKSwitch">false</property>

       <!-- XA Recovery Log Log path -->
       <!--<property name="XARecoveryLogBaseDir">./</property>-->

       <!-- XA Recovery Log Log name -->
       <!--<property name="XARecoveryLogBaseName">tmlog</property>-->
       <!--If yes true Strictly observe the isolation level if necessary,Not just select Statement to switch connections in a transaction-->
       <property name="strictTxIsolation">false</property>
       <!--If 0,Involving multiple DataNode of catlet Tasks do not execute across threads-->
       <property name="parallExecute">0</property>
   </system>
Copy code

Firewall configuration

<firewall>
   <whitehost>
      <host host="1*7.0.0.*" user="root"/>
   </whitehost>
   <blacklist check="false">
   </blacklist>
</firewall>
Copy code

2.2 Server.xml detail parsing

Server. There are several tags inside XML, mainly user, system and firewall

// User: used to define the user and permission of the logged in Mycat, which is finally mapped to UserConfig. In addition to the above, it also provides the following special attributes: 
- benchmark : Load balancing strategy , 0 The number of connections is unlimited
- privilegesConfig : Table level addition, deletion, modification and query settings


// System tag: system configuration tag, which is finally mapped to SystemConfig object. In addition to the above, it mainly has the following configurations
- charset : Configure character set , Be sure to be consistent with the database
- defaultSqlParser : Specifies the default parser
- processors : Number of threads available to the system (default Runtime.getRuntime().availableProcessors())
- processorBufferChunk : Each allocation Socket Direct Buffer , Here, the size of bytes obtained will be affected
- processorBufferPool : BufferPool Calculation proportion of
- processorBufferLocalPercent : ThreadLocalPool distribution Pool Scale size of , Default 100
- sequnceHandlerType : Mycat Type of global sequence

- private long processorCheckPeriod : clear NIOProcessor Idle interval
- private long dataNodeIdleCheckPeriod :  Back end connection cleanup interval
- private long dataNodeHeartbeatPeriod :  Initiate heartbeat interval for backend read / write
- private int useOffHeapForMerge : Enable Off Heap for Merge  1-Enabled, 0-Not enabled


- private int usingAIO = 0 : Open AIO 
- private int packetHeaderSize = 4 : MySQL Protocol message length
- private int maxPacketSize = 16 * 1024 * 1024 : Maximum size of data that can be carried
- private String memoryPageSize : Page size,corresponding MemoryBlock The size of the, in M
- private long idleTimeout : The timeout length of the idle time of the connection
- private int txIsolation : The isolation level of the initial front-end connection transaction , Corresponding 1-4

Copy code

III Server. How to load XML

Server.xml is loaded in the ConfigInitializer. Finally, the bit Map is loaded and passed to MycatServer

//Read server xml
XMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader);

// Corresponding schema xml -> dataHost
private final Map<String, DataHostConfig> dataHosts;
// Corresponding schema xml -> dataNode
private final Map<String, DataNodeConfig> dataNodes;
// Corresponding schema XML rules
private final Map<String, SchemaConfig> schemas;
// Corresponding System global configuration
private final SystemConfig system;
// Corresponding User specific Config
private final Map<String, UserConfig> users;
// Corresponding firewall configuration
private final FirewallConfig firewall;
// Corresponding slice configuration
private final ClusterConfig cluster;
Copy code

IV Configured usage scenarios

4.1 node 1: Server connection

When connecting through the tool, the init DB operation will be performed first, and the DB information will be obtained to obtain the connection

C- FrontendConnection # initDB
public void initDB(byte[] data) {
   
   // S1: get data through MySQLMessage object
   // init request parameters: \ u0006 \ u0000 \ u0000 \ u0000 \ u0002db001 - > DB001

   // S2: check DB validity
   db == null || !privileges.schemaExists(db)
   
   // S3: verify whether the current user exists
   privileges.userExists(user, host)
 
   // S4: get Schemas
   Set<String> schemas = privileges.getUserSchemas(user);
   if (schemas == null || schemas.size() == 0 || schemas.contains(db)) {
      this.schema = db;
      // OkPacket.OK : 07 00 00 02 00 00 00 02 00 00 00
      write(writeToBuffer(OkPacket.OK, allocate()));
   } else {
      String s = "Access denied for user '" + user + "' to database '" + db + "'";
      writeErrMessage(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);
   }
} 

Copy code

Supplement 1: okpocket OK: what does 700010002000 mean? MySQL OK package return structure

An OK packet is sent from the server to the client, indicating the successful completion of the command. In MySQL 5.7.5, OK packets are also used to indicate EOF, and the EOF packet has been discarded.

The packages will contain the following data: packet header, affected rows, last inserted ID, status flag status, etc. there will be an opportunity to find a case to see in detail later

Supplement 2: privileges object

As you can see, FrontendPrivileges frequently appears in the above code. What is the function of this object?

FrontendPrivileges is a permission provider interface. It provides several common methods, such as:

  • schemaExists: check whether the schema exists
  • userExists: check whether the user exists and can use host to implement isolation policy
  • checkFirewallWhiteHostPolicy: check firewall policy

.... wait

It can be understood that FrontendPrivileges is for server The main implementation class of logical processing of XML data is MycatPrivileges

Its internal logic is also relatively simple, mainly dealing with MycatConfig, for example:

// Check whether the schema exists
public boolean schemaExists(String schema) {
    MycatConfig conf = MycatServer.getInstance().getConfig();
    return conf.getSchemas().containsKey(schema);
}


// Very good practice, directly return the instance object 
public static final MycatServer getInstance() {
    // private static final MycatServer INSTANCE = new MycatServer();
    // The static object has completed the initialization of related config in the constructor
    return INSTANCE;
}

Copy code

You can see that the internal configuration basically includes the common Config

4.2 node 2: configuration during query

The configuration will take effect in each phase. Here are some common scenarios:

Common usage scenarios of timeout

// Regularly check tasks and handle recycled resources: 
C- MycatServer # processorCheck : 
C- NIOProcessor # backendCheck
private void backendCheck() {
    // S1: get timeout
   long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;
   
   // S2: iterate over all connections
   Iterator<Entry<Long, BackendConnection>> it = backends.entrySet().iterator();

   // S3: first delete the empty connection, and then close the connection with SQL execution timeout
  if (c.isBorrowed() && c.getLastTime() < TimeUtil.currentTimeMillis() - sqlTimeout) {
      //.....
  }
     
}

// PS: there are many usage scenarios, so I won't look at them one by one here

Copy code

From this case, it is obvious that the configuration is mainly processed by obtaining the configuration from MycatServer, and MycatServer is one of the core objects of the whole process. Let's take a detailed look in the next article

V Data reception of Server

When Mycat NIO processes the request, you can see a Byte [] continuous flow. What data does this Byte contain?

// In FrontendConnection # initDB, we can see the specific data of data. Take a connection as an example: 
- \u0006\u0000\u0000\u0000\u0002 : prefix , Judge the specific type
- db001 : concrete Server Schema 

// Supplement: judge the C- FrontendCommandHandler # handle by the first four types
public void handle(byte[] data) {
    if (source.getLoadDataInfileHandler() != null && source.getLoadDataInfileHandler().isStartLoadData()) {
        MySQLMessage mm = new MySQLMessage(data);
        int packetLength = mm.readUB3();
        if (packetLength + 4 == data.length) {
            source.loadDataInfileData(data);
        }
        return;
    }
    
    // Can see
    switch (data[4]) {
        // public static final byte COM_INIT_DB = 2;
        case MySQLPacket.COM_INIT_DB:
            commands.doInitDB();
            source.initDB(data);
            break;
        // public static final byte COM_QUERY = 3;    
        case MySQLPacket.COM_QUERY:
            commands.doQuery();
            source.query(data);
            break;
        case MySQLPacket.COM_PING:
            commands.doPing();
            source.ping();
            break;
        case MySQLPacket.COM_QUIT:
            commands.doQuit();
            source.close("quit cmd");
            break;
        //.....  Omit part type
        default:
            commands.doOther();
            MycatConfig config = MycatServer.getInstance().getConfig();
            if (config.getSystem().getIgnoreUnknownCommand() == 1) {
                LOGGER.warn("Unknown command:{}", data[4]);
                source.ping();
            } else {
                LOGGER.error("Unknown command:{}", new String(data));
                source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,
                        "Unknown command");
            }
    }
}
Copy code

Generally, a query is divided into two parts:

  • Initialize connection
  • Execute the current operation, taking Query as an example

It can be seen here that Byte[0] - Byte[4] is still a flag bit, and specific SQL will be passed in later Whether initDB or Query, a key step will be taken:

MySQLMessage mm = new MySQLMessage(data);
// Parsing flag bit
mm.position(5);
String db = mm.readString();


// When the label bit parsing is completed, the corresponding Handler will be called to complete the subsequent logic
- queryHandler.query(sql);

Copy code

summary

After a simple study of the entrance, reference to the documents and some supplements, you can slowly go deep into the whole system in the follow-up

last

If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts

If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu Thank you very much!

PHP learning manual: https://doc.crmeb.com
Technical exchange forum: https://q.crmeb.com

Topics: PHP MySQL github mycat .NET