未验证 提交 2d94ac0c 编写于 作者: H He Wang 提交者: GitHub

add Chinese documents (#61)

add Chinese README and tutorial doc
上级 bf0e81d8
......@@ -4,6 +4,12 @@ on:
push:
branches:
- master
paths-ignore:
- '.github/**'
- 'docs/**'
- 'scripts/**'
- '**.md'
- '.*'
jobs:
publish-snapshot:
......
OceanBase Log Client
--------------------
OceanBase Log Client is a library for obtaining log of [OceanBase](https://github.com/oceanbase/oceanbase).
English | [简体中文](README_CN.md)
[![Build Status](https://github.com/oceanbase/oblogclient/actions/workflows/maven_publish_snapshot.yml/badge.svg?branch=master)](https://github.com/oceanbase/oblogclient/actions/workflows/maven_publish_snapshot.yml)
[![Release](https://img.shields.io/github/release/oceanbase/oblogclient.svg)](https://github.com/oceanbase/oblogclient/releases)
[![License](https://img.shields.io/badge/license-Mulan%20PSL%20v2-green.svg)](LICENSE)
OceanBase Log Client is a library for obtaining the commit log of [OceanBase](https://github.com/oceanbase/oceanbase).
Getting Started
---------------
### Work with LogProxy
You can use `logproxy-client` with [oblogproxy](https://github.com/oceanbase/oblogproxy) to get the commit log of OceanBase cluster, see the [tutorial](docs/quickstart/logproxy-client-tutorial.md) for more details.
You can use this project with [oblogproxy](https://github.com/oceanbase/oblogproxy) to get the commit log of OceanBase. See the [LogProxy client tutorial](docs/quickstart/logproxy-client-tutorial.md) for more details.
### Connect to OceanBase Directly
......@@ -16,8 +22,8 @@ Coming soon.
Communication
---------------
* [Official Q&A Website (Chinese)](https://open.oceanbase.com/answer) (Q&A, ideas, general discussion)
* [GitHub Issues](https://github.com/oceanbase/oblogclient/issues) (Bug reports, feature requests)
* [Official Forum](https://ask.oceanbase.com/) (Q&A, ideas, general discussion)
* DingTalk Group (Q&A, general discussion): 33254054
License
......
OceanBase 日志客户端
--------------------
[English](README.md) | 简体中文
[![Build Status](https://github.com/oceanbase/oblogclient/actions/workflows/maven_publish_snapshot.yml/badge.svg?branch=master)](https://github.com/oceanbase/oblogclient/actions/workflows/maven_publish_snapshot.yml)
[![Release](https://img.shields.io/github/release/oceanbase/oblogclient.svg)](https://github.com/oceanbase/oblogclient/releases)
[![License](https://img.shields.io/badge/license-Mulan%20PSL%20v2-green.svg)](LICENSE)
OceanBase 日志客户端是一个可用于获取 [OceanBase](https://github.com/oceanbase/oceanbase) 增量数据变动 clog (commit log)的 Java 依赖库。
开始上手
---------------
### 搭配 LogProxy 使用
你可以搭配 [oblogproxy](https://github.com/oceanbase/oblogproxy) 来获取 OceanBase 的 clog。更多信息可以查看 [LogProxy 客户端使用教程](docs/quickstart/logproxy-client-tutorial-cn.md)
### 直连 OceanBase 数据库
规划中,敬请期待。
沟通交流
---------------
* [GitHub Issues](https://github.com/oceanbase/oblogclient/issues) ( bug 或请求新的功能)
* [官方论坛](https://ask.oceanbase.com/) (答疑和讨论)
* 钉钉群 (答疑和讨论): 33254054
协议
-------
本项目使用[木兰宽松许可证, 第2版](LICENCE)
# LogProxy 客户端使用教程
[oblogproxy](https://github.com/oceanbase/oblogproxy) (OceanBase Log Proxy,以下称 LogProxy)是一个获取 OceanBase 增量 clog (commit log)的代理服务。本教程将向你展示如何使用 LogProxy 客户端连接 LogProxy ,并通过该连接获取日志数据。
## 准备
使用之前需要做的准备:
1. 安装 JDK 1.8 或更新的版本。
2. 启动 LogProxy 服务。
3. 如果 LogProxy 服务开启了 SSL 验证,需要准备好相关的证书文件。
4. 安装 Maven 或 Gradle,否则的话需要手动下载依赖的所有 jar 文件。
## 依赖配置
所有已发布的版本都可以在 [Maven 中央仓库](https://mvnrepository.com/artifact/com.oceanbase/oblogclient-logproxy)找到,你也可以选择从[归档目录](https://repo1.maven.org/maven2/com/oceanbase/oblogclient-logproxy/)手动下载。
如下所示是使用 Maven 时的示例:
```xml
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>oblogclient-logproxy</artifactId>
<version>x.y.z</version>
</dependency>
```
如果你想要使用最新的快照版本,可以通过配置 Maven 快照仓库来指定:
```xml
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>oblogclient-logproxy</artifactId>
<version>x.y.z-SNAPSHOT</version>
</dependency>
<repositories>
<repository>
<id>sonatype-snapshots</id>
<name>Sonatype Snapshot Repository</name>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
```
## 使用
### 基本使用
连接 LogProxy 服务时,需要配置一些 `ObReaderConfig` 类的参数:
<div class="highlight">
<table class="colwidths-auto docutils">
<thead>
<tr>
<th class="text-left" style="width: 10%">参数名</th>
<th class="text-left" style="width: 8%">是否必需</th>
<th class="text-left" style="width: 7%">默认值</th>
<th class="text-left" style="width: 10%">类型</th>
<th class="text-left" style="width: 15%">设置函数</th>
<th class="text-left" style="width: 50%">参数说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>cluster_url</td>
<td></td>
<td style="word-wrap: break-word;">空字符串</td>
<td>String</td>
<td>setClusterUrl</td>
<td>用于获取 OceanBase 集群节点信息的 url,当且仅当使用企业版的 OceanBase 时需要设置。使用 sys 用户执行 <code>show parameters like 'obconfig_url'</code> 时,返回的 value 字段即为该值。</td>
</tr>
<tr>
<td>rootserver_list</td>
<td></td>
<td style="word-wrap: break-word;">空字符串</td>
<td>String</td>
<td>setRsList</td>
<td>OceanBase 集群的节点列表,当且仅当使用社区版的 OceanBase 时需要设置。使用 sys 用户执行 <code>show parameters like 'rootservice_list'</code> 时,返回的 value 字段即为该值。</td>
</tr>
<tr>
<td>cluster_user</td>
<td></td>
<td style="word-wrap: break-word;">空字符串</td>
<td>String</td>
<td>setUsername</td>
<td>连接 OceanBase 的用户名,格式一般为 <code>用户名@租户名</code></td>
</tr>
<tr>
<td>cluster_password</td>
<td></td>
<td style="word-wrap: break-word;">空字符串</td>
<td>String</td>
<td>setPassword</td>
<td>连接 OceanBase 的密码。</td>
</tr>
<tr>
<td>tb_white_list</td>
<td></td>
<td style="word-wrap: break-word;">*.*.*</td>
<td>String</td>
<td>setTableWhiteList</td>
<td>监听的数据变动白名单,使用 fnmatch 按照格式 <code>租户.库.表</code> 进行匹配,多个值使用 <code>|</code> 分隔。需要注意的是,当前使用的用户需要至少对监听的范围有 SELECT 权限。</td>
</tr>
<tr>
<td>tb_black_list</td>
<td></td>
<td style="word-wrap: break-word;"></td>
<td>String</td>
<td>setTableBlackList</td>
<td>监听的数据变动黑名单,使用 fnmatch 按照格式 <code>租户.库.表</code> 进行匹配,多个值使用 <code>|</code> 分隔。</td>
</tr>
<tr>
<td>first_start_timestamp</td>
<td></td>
<td style="word-wrap: break-word;">0</td>
<td>Long</td>
<td>setStartTimestamp</td>
<td>获取数据的起点时间戳,单位是秒。为 0 时将从当前时刻开始获取。</td>
</tr>
<tr>
<td>timezone</td>
<td></td>
<td style="word-wrap: break-word;">+08:00</td>
<td>String</td>
<td>setTimezone</td>
<td>连接使用的时区,该取值将会影响时间类型的字段读取。</td>
</tr>
<tr>
<td>working_mode</td>
<td></td>
<td style="word-wrap: break-word;">storage</td>
<td>String</td>
<td>setWorkingMode</td>
<td>libobcdc 的工作模式,可选值为 "storage" 和 "memory"。</td>
</tr>
</tbody>
</table>
</div>
除了这些之外,还有其他参数可以通过 `ObReaderConfig` 类的构造函数进行配置,更多信息可以在[官方文档网站](https://www.oceanbase.com/docs/) 搜索 obcdc 获取。
下面是一个使用社区版 OceanBase 时配置 `ObReaderConfig` 的例子:
```java
ObReaderConfig config = new ObReaderConfig();
config.setRsList("127.0.0.1:2882:2881");
config.setUsername("user@tenant");
config.setPassword("password");
config.setStartTimestamp(0L);
config.setTableWhiteList("tenant.*.*");
```
如果使用企业版 OceanBase,可以参考如下配置:
```java
ObReaderConfig config = new ObReaderConfig();
config.setClusterUrl("http://127.0.0.1:8080/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=ocpmaster&ObRegion=tenant");
config.setUsername("user@tenant");
config.setPassword("password");
config.setStartTimestamp(0L);
config.setTableWhiteList("tenant.*.*");
```
`ObReaderConfig` 配置完成后,就可以创建 LogProxy 客户端实例并开始监听数据变动了。
```java
LogProxyClient client = new LogProxyClient("127.0.0.1", 2983, config);
// 绑定一个处理日志数据的 RecordListener
client.addListener(new RecordListener() {
@Override
public void notify(LogMessage message){
// 在此添加数据处理逻辑
}
@Override
public void onException(LogProxyClientException e) {
logger.error(e.getMessage());
}
});
client.start();
client.join();
```
调用函数 `LogProxyClient.start()` 会启动一个新的线程,该线程将使用 Netty 建立一个到 LogProxy 的连接,并通过该连接接收日志数据。
LogProxy 客户端还可以通过 `ClientConf` 配置一些客户端行为相关的参数,如下所示是一个使用自定义 `ClientConf` 的例子:
```java
ClientConf clientConf =
ClientConf.builder()
.clientId("myClientId")
.transferQueueSize(1024)
.connectTimeoutMs(1000)
.readWaitTimeMs(1000)
.retryIntervalS(1)
.maxReconnectTimes(10)
.idleTimeoutS(10)
.nettyDiscardAfterReads(1)
.ignoreUnknownRecordType(true)
.build();
LogProxyClient client = new LogProxyClient("127.0.0.1", 2983, config, clientConf);
```
客户端会将接收到的日志数据转为 `LogMessage`,更多信息可以参考 [LogMessage 介绍](../formats/logmessage-cn.md)
完整的使用示例可以参考 [LogProxyClientTest.java](../../oblogclient-logproxy/src/test/java/com/oceanbase/clogproxy/client/LogProxyClientTest.java)
### SSL 验证
如果 LogProxy 开启了 SSL 验证,在启动 LogProxy 客户端时将需要配置 [SslContext](https://netty.io/4.1/api/io/netty/handler/ssl/SslContext.html)
```java
SslContext sslContext = SslContextBuilder.forClient()
.sslProvider(SslContext.defaultClientProvider())
.trustManager(this.getClass().getClassLoader().getResourceAsStream("ca.crt"))
.keyManager(
this.getClass().getClassLoader().getResourceAsStream("client.crt"),
this.getClass().getClassLoader().getResourceAsStream("client.key"))
.build();
ClientConf clientConf = ClientConf.builder().sslContext(sslContext).build();
LogProxyClient client = new LogProxyClient("127.0.0.1", 2983, config, clientConf);
```
### 版本兼容性
##### 数据压缩
LogProxy 社区版从 1.0.1 开始默认会对发送到客户端的数据进行压缩,正确解压缩需要使用客户端 1.0.4 或之后的版本。
##### 使用 Client ID
LogProxy 使用 `ClientConf` 中的 `clientId` 来区分不同的连接,若想在并发环境下使用本客户端,或者想复用 clientId,需要使用客户端 1.0.4 或之后的版本。
## 问题排查
当 LogProxy 与客户端之间的连接建立成功后,LogProxy 将会开始向客户端发送日志数据,这里的日志数据主要有心跳和数据变动两类。也就是说,及时数据库在监听范围内没有变动,LogProxy 客户端也应当能收到心跳类型的数据。
如果 LogProxy 客户端启动后,没有报错信息出现,也没有收到任何数据,这时候为了确定问题出现的原因,需要查看 LogProxy 对应的 LogReader 子进程的状态,相关的信息在 LogProxy 部署目录的 `run/{clientId}/`
# LogProxyClient Tutorial
[oblogproxy](https://github.com/oceanbase/oblogproxy) (OceanBase Log Proxy, hereinafter LogProxy) is a proxy service which can fetch incremental log data from OceanBase. This tutorial will show you how to use LogProxyClient to connect to LogProxy and get the log data.
[oblogproxy](https://github.com/oceanbase/oblogproxy) (OceanBase Log Proxy, hereinafter LogProxy) is a proxy service which can fetch the clog (commit log) data from OceanBase. This tutorial will show you how to use LogProxy client to connect to LogProxy and get the log data.
## Preparation
There are some requirements to use LogProxyClient:
There are some requirements:
1. JDK 1.8 or higher version installed.
2. LogProxy started.
3. SSL certificate files if SSL encryption is enabled.
4. Maven or Gradle installed, otherwise you need download the jar file manually.
4. Maven or Gradle installed, otherwise you need download the jar files manually.
## Binaries/Download
Releases are available in the [Maven Central](https://mvnrepository.com/artifact/com.oceanbase.logclient/logproxy-client), you can also download the jar file manually from the [archive](https://repo1.maven.org/maven2/com/oceanbase/logclient/logproxy-client/).
Releases are available in the [Maven Central](https://mvnrepository.com/artifact/com.oceanbase/oblogclient-logproxy), you can also download the jar file manually from the [archive](https://repo1.maven.org/maven2/com/oceanbase/oblogclient-logproxy/).
Here is an example for Maven:
```xml
<dependency>
<groupId>com.oceanbase.logclient</groupId>
<artifactId>logproxy-client</artifactId>
<groupId>com.oceanbase</groupId>
<artifactId>oblogclient-logproxy</artifactId>
<version>x.y.z</version>
</dependency>
```
......@@ -30,8 +29,8 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
```xml
<dependency>
<groupId>com.oceanbase.logclient</groupId>
<artifactId>logproxy-client</artifactId>
<groupId>com.oceanbase</groupId>
<artifactId>oblogclient-logproxy</artifactId>
<version>x.y.z-SNAPSHOT</version>
</dependency>
......@@ -51,37 +50,116 @@ If you'd rather like the latest snapshots of the upcoming major version, use our
### Basic Usage
To connect to LogProxy, there are some parameters to set in `ObReaderConfig`:
- *cluster_url*: Cluster config url used to set up the OBConfig service. Required for OceanBase Enterprise Edition.
- *rootserver_list*: Root server list of OceanBase cluster in format `ip1:rpc_port1:sql_port1;ip2:rpc_port2:sql_port2`, IP address here must be able to be resolved by LogProxy. Required for OceanBase Community Edition.
- *cluster_username*: Username for OceanBase, the format is `username@tenant_name#cluster_name` when connecting to [obproxy](https://github.com/oceanbase/obproxy) or `username@tenant_name` when directly connecting to OceanBase server.
- *cluster_password*: Password for OceanBase when using configured `cluster_username`.
- *first_start_timestamp*: Start timestamp in seconds, and zero means starting from now. Default is `0`.
- *tb_white_list*: Table whitelist in format `tenant_name.database_name.table_name`. Pattern matching is provided by `fnmatch`, and multiple values can be separated by `|`. Default is `*.*.*`.
- *tb_black_list*: Table blacklist in the same format with whitelist. Default is `|`.
- *timezone*: Timezone offset from UTC. Default value is `+8:00`.
- *working_mode*: Working mode. Can be `storage` (default value, supported from `obcdc` 3.1.3) or `memory`.
These parameters are used in `obcdc` (former `liboblog`), and the items not listed above can be passed to `obcdc` through the `ObReaderConfig` constructor with parameters.
To connect to LogProxy, there are some options in `ObReaderConfig`:
<div class="highlight">
<table class="colwidths-auto docutils">
<thead>
<tr>
<th class="text-left" style="width: 10%">Option</th>
<th class="text-left" style="width: 8%">Required</th>
<th class="text-left" style="width: 7%">Default</th>
<th class="text-left" style="width: 10%">Type</th>
<th class="text-left" style="width: 15%">Setter</th>
<th class="text-left" style="width: 50%">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>cluster_url</td>
<td>false</td>
<td style="word-wrap: break-word;">Empty</td>
<td>String</td>
<td>setClusterUrl</td>
<td>The url used to get information about OceanBase servers. Query with <code>show parameters like 'obconfig_url'</code> using user of `sys` tenant, and you can get it at the `value` field.</td>
</tr>
<tr>
<td>rootserver_list</td>
<td>false</td>
<td style="word-wrap: break-word;">Empty</td>
<td>String</td>
<td>setRsList</td>
<td>The OceanBase server list. Query with <code>show parameters like 'rootservice_list'</code> using user of `sys` tenant, and you can get it at the `value` field.</td>
</tr>
<tr>
<td>cluster_user</td>
<td>true</td>
<td style="word-wrap: break-word;">Empty</td>
<td>String</td>
<td>setUsername</td>
<td>Username for OceanBase, the format is <code>username@tenant_name</code>.</td>
</tr>
<tr>
<td>cluster_password</td>
<td>true</td>
<td style="word-wrap: break-word;">Empty</td>
<td>String</td>
<td>setPassword</td>
<td>Password for OceanBase.</td>
</tr>
<tr>
<td>tb_white_list</td>
<td>false</td>
<td style="word-wrap: break-word;">*.*.*</td>
<td>String</td>
<td>setTableWhiteList</td>
<td>Table whitelist in format <code>tenant_name.database_name.table_name</code>. Pattern matching is provided by fnmatch, and multiple values can be separated by <code>|</code>. Note that the user should have at least the SELECT privilege on this whitelist.</td>
</tr>
<tr>
<td>tb_black_list</td>
<td>false</td>
<td style="word-wrap: break-word;"></td>
<td>String</td>
<td>setTableBlackList</td>
<td>Table blacklist in format <code>tenant_name.database_name.table_name</code>. Pattern matching is provided by fnmatch, and multiple values can be separated by <code>|</code>. </td>
</tr>
<tr>
<td>first_start_timestamp</td>
<td>false</td>
<td style="word-wrap: break-word;">0</td>
<td>Long</td>
<td>setStartTimestamp</td>
<td>Timestamp of the starting point of data in seconds, and zero means starting from now.</td>
</tr>
<tr>
<td>timezone</td>
<td>false</td>
<td style="word-wrap: break-word;">+08:00</td>
<td>String</td>
<td>setTimezone</td>
<td>Timezone used to convert data of temporal types.</td>
</tr>
<tr>
<td>working_mode</td>
<td>false</td>
<td style="word-wrap: break-word;">storage</td>
<td>String</td>
<td>setWorkingMode</td>
<td>Working mode of libobcdc, can be 'storage' or 'memory'.</td>
</tr>
</tbody>
</table>
</div>
There are some other options that can be set with the constructor of `ObReaderConfig`, you can search obcdc on the [doc website](https://www.oceanbase.com/docs/) for more details.
Here is an example to set `ObReaderConfig` with OceanBase Community Edition:
```java
ObReaderConfig config = new ObReaderConfig();
config.setRsList("127.0.0.1:2882:2881");
config.setUsername("username");
config.setUsername("user@tenant");
config.setPassword("password");
config.setStartTimestamp(0L);
config.setTableWhiteList("tenant.*.*");
```
If you want to work with OceanBase Enterprise Edition, you can set the `ObReaderConfig` with `cluster_url` like below:
And you can set `ObReaderConfig` for OceanBase Enterprise Edition like below:
```java
ObReaderConfig config = new ObReaderConfig();
config.setClusterUrl("http://127.0.0.1:8080/services?Action=ObRootServiceInfo&User_ID=alibaba&UID=ocpmaster&ObRegion=tenant");
config.setUsername("username");
config.setUsername("user@tenant");
config.setPassword("password");
config.setStartTimestamp(0L);
config.setTableWhiteList("tenant.*.*");
......@@ -132,6 +210,8 @@ LogProxyClient client = new LogProxyClient("127.0.0.1", 2983, config, clientConf
The received log records are parsed to `LogMessage` in the client handler, you can see [LogMessage doc](../formats/logmessage.md) for more details.
To get a full example, you can check the [LogProxyClientTest.java](../../oblogclient-logproxy/src/test/java/com/oceanbase/clogproxy/client/LogProxyClientTest.java).
### SSL Encryption
If SSL verification is enabled at LogProxy, you should instance a LogProxyClient with [SslContext](https://netty.io/4.1/api/io/netty/handler/ssl/SslContext.html). For example:
......@@ -148,31 +228,18 @@ ClientConf clientConf = ClientConf.builder().sslContext(sslContext).build();
LogProxyClient client = new LogProxyClient("127.0.0.1", 2983, config, clientConf);
```
Here you need provide following files:
- *ca.crt*: Trusted certificate for verifying the remote endpoint's certificate, should be same with LogProxy.
- *client.crt*: Certificate of this client.
- *client.key*: Private key of this client.
See [manual](https://github.com/oceanbase/oblogproxy/blob/master/docs/manual.md) of LogProxy for more details about SSL encryption.
## Version Compatibility
The communication protocol between the `logproxy-client` and `oblogproxy` is forward compatible, and the latest version of `logproxy-client` can work with any version of `oblogproxy`. But for legacy versions, there are some restrictions in functionality.
#### OceanBase Enterprise Edition
To monitor change data from OceanBase EE, you need to configure `cluster_url` to replace the `rootserver_list` parameter for `obcdc`, which is supported from `1.0.4` of the client.
### Version Compatibility
#### Record Compression
The log proxy compresses the record data by default from `1.0.1`, and the client fixed the bug in decompression process with [#33](https://github.com/oceanbase/oblogclient/pull/33) from `1.0.4`. So if you want to work with log proxy `1.0.1` or later version, you should use `1.0.4` or later version of the client.
The log proxy compresses the record data by default from `1.0.1`, and to decompress the data properly, you should use `1.0.4` or later version of the client.
#### Reuse Client Id
#### Use Client Id
The log proxy use `clientId` to identify a connection, and reuse it will make the log proxy reduce the use of hardware resources. In legacy versions of the client, there is a bug [#38](https://github.com/oceanbase/oblogclient/issues/38) which will cause connection close failure, and it's fixed in `1.0.4`. So you can only reuse a fixed `clientId` from `1.0.4` of the client.
The LogProxy use `clientId` to identify a connection, if you want to use the client concurrently, or you want to reuse the `clientId`, you should use `1.0.4` or later version of the client.
## Heartbeat and Troubleshooting
## Troubleshooting
Once the connection is established properly, LogProxy will start to fetch log messages from OceanBase and send them to LogProxyClient. When the connection is idle, LogProxy will send heartbeat messages to LogProxyClient.
When the connection between LogProxy and the client is successfully established, LogProxy will send log data to the client. The log data here mainly includes heartbeat and data changes. Even if the database does not change within the monitoring scope, the LogProxy client should be able to receive heartbeat data.
Note that when LogProxy receives the ObReaderConfig, it will only check the format but won't verify the content. So only when the LogProxyClient receives log messages or heartbeat messages can we be sure that the connection is established properly. If the connection doesn't work properly and there is no error message in log of LogProxyClient, you need to check the log of `oblogreader` at LogProxy side, which is under `$(logproxy_home)/run` by default.
If the LogProxy client does not receive data and no error message appears after startup, in order to determine the cause of the issue, you should check the LogReader thread of the LogProxy for this connection, and the working space of LogReader is `run/ {clientId}/`.
......@@ -62,7 +62,7 @@ public class ObReaderConfig extends AbstractConnectionConfig {
new ConfigItem<>("first_start_timestamp_us", 0L);
/** Timezone offset. */
private final ConfigItem<String> timezone = new ConfigItem<>("timezone", "+8:00");
private final ConfigItem<String> timezone = new ConfigItem<>("timezone", "+08:00");
/** Working mode. */
private final ConfigItem<String> workingMode = new ConfigItem<>("working_mode", "storage");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册