.NetCore实践篇:分布式监控Zipkin持久化之殇(四)

前言

本系列已写了四篇文章,读本篇之前,可以先读前面几篇。
思考大纲:.Net架构篇:思考如何设计一款实用的分布式监控系统?
实践篇一:.NetCore实践篇:分布式监控客户端ZipkinTracer从入门到放弃之路
实践篇二:.NetCore实践篇:分布式监控系统zipkin踩坑之路(二)
实践篇三:.NetCore实践篇:成功解决分布式监控ZipKin聚合依赖问题(三)

简要回顾

zipkin

Zipkin是一种分布式跟踪系统。它有助于收集解决微服务架构中的延迟问题所需的时序数据
zipkin官网

zipkin4Net

zipkin4net是.NET客户端库。
zipkin4net

zipkin-dependencies

这是一个Spark作业,它将从您的数据存储区收集跨度,分析服务之间的链接,并存储它们以供以后在Web UI中呈现。

使用方法

如果内存不足时,java后跟上-Xmx1024m -Xms1024m参数,JAVA_OPTS的一些参数可参考Oracle官方说明配置默认JVM和Java参数

# ex to run the job to process yesterday's traces on OS/X
$ STORAGE_TYPE=cassandra3 java -jar zipkin-dependencies.jar `date -uv-1d +%F`
# or on Linux
$ STORAGE_TYPE=cassandra3 java -jar zipkin-dependencies.jar `date -u -d '1 day ago' +%F`

MySQL 存储

* `MYSQL_DB`: 使用的数据库,默认是 "zipkin".
* `MYSQL_USER` and `MYSQL_PASS`: MySQL授权, 默认是空.
* `MYSQL_HOST`: 默认主机(域名/ip)是localhost
* `MYSQL_TCP_PORT`: 默认端口是 3306
* `MYSQL_USE_SSL`: 验证 `javax.net.ssl.trustStore` 和 `javax.net.ssl.trustStorePassword`,默认不验证。

示例

$ STORAGE_TYPE=mysql MYSQL_USER=root java -jar zipkin-dependencies.jar

详情参考:
zipkin-dependencies

实践

创建使用数据库

mysql> create database mytestdb;
Query OK, 1 row affected (0.01 sec)
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| mytestdb           |
| performance_schema |
| sys                |
| ttt                |
+--------------------+
7 rows in set (0.00 sec)
mysql> use mytestdb
Database changed

使用sql语句创建zipkin表

CREATETABLE IF NOT EXISTS zipkin_spans (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64bit',
  `trace_id` BIGINT NOT NULL,
  `id` BIGINT NOT NULL,
  `name` VARCHAR(255) NOT NULL,
  `parent_id` BIGINT,
  `debug` BIT(1),
  `start_ts` BIGINT COMMENT 'Span.timestamp():epoch micros used for endTs query and to implement TTL',
  `duration` BIGINT COMMENT 'Span.duration():micros used for minDuration and maxDuration query'
)ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
ALTERTABLE zipkin_spans ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `id`) COMMENT'ignore insert on duplicate';
ALTERTABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`, `id`) COMMENT 'forjoining with zipkin_annotations';
ALTERTABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'forgetTracesByIds';
ALTERTABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTERTABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering andrange';
 
CREATETABLE IF NOT EXISTS zipkin_annotations (
  `trace_id_high` BIGINT NOT NULL DEFAULT 0COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64bit',
  `trace_id` BIGINT NOT NULL COMMENT 'coincideswith zipkin_spans.trace_id',
  `span_id` BIGINT NOT NULL COMMENT 'coincideswith zipkin_spans.id',
  `a_key` VARCHAR(255) NOT NULL COMMENT'BinaryAnnotation.key or Annotation.value if type == -1',
  `a_value` BLOB COMMENT'BinaryAnnotation.value(), which must be smaller than 64KB',
  `a_type` INT NOT NULL COMMENT'BinaryAnnotation.type() or -1 if Annotation',
  `a_timestamp` BIGINT COMMENT 'Used toimplement TTL; Annotation.timestamp or zipkin_spans.timestamp',
  `endpoint_ipv4` INT COMMENT 'Null whenBinary/Annotation.endpoint is null',
  `endpoint_ipv6` BINARY(16) COMMENT 'Null whenBinary/Annotation.endpoint is null, or no IPv6 address',
  `endpoint_port` SMALLINT COMMENT 'Null whenBinary/Annotation.endpoint is null',
  `endpoint_service_name` VARCHAR(255) COMMENT'Null when Binary/Annotation.endpoint is null'
)ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
ALTERTABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`,`a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTERTABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`)COMMENT 'for joining with zipkin_spans';
ALTERTABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'forgetTraces/ByIds';
ALTERTABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'forgetTraces and getServiceNames';
ALTERTABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
ALTERTABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
ALTERTABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'fordependencies job';
 
CREATETABLE IF NOT EXISTS zipkin_dependencies (
  `day` DATE NOT NULL,
  `parent` VARCHAR(255) NOT NULL,
  `child` VARCHAR(255) NOT NULL,
  `call_count` BIGINT
)ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
 
ALTERTABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);

创建成功后,查询结果。

mysql> show tables;
+---------------------+
| Tables_in_mytestdb  |
+---------------------+
| zipkin_annotations  |
| zipkin_dependencies |
| zipkin_spans        |
+---------------------+
3 rows in set (0.00 sec)

启动zipkin-dependencies

最开始我的密码是【四个字母一个感叹号一个数字】,再执行启动命令时,密码那块给我报错自动换成【四个字母rm -f】,我修改成【四个字母一个#号一个数字】就能执行了

执行成功后,依然提示Access denied for user 'root'@'localhost' (using password: NO),但我在linux的命令中直接用mysql -u root -p相同密码是可以登录成功的。所以问题出现在哪呢?

[root@izwz9fwifc2eniq3lbdzmgz cusD]# STORAGE_TYPE=mysql MYSQL_HOST=localhost MYSQL_TCP_PORT=3306 MYSQL_DB=mytestdb MYSQL_USER=XXXXX MYSQL_PASS=XXXXX java -Xmx1024m -Xms1024m  -jar  zipkin-dependencies.jar 


Exception in thread "main" java.lang.RuntimeException: java.sql.SQLInvalidAuthorizationSpecException: Access denied for user 'root'@'localhost' (using password: NO)
    at zipkin2.dependencies.mysql.MySQLDependenciesJob.hasTraceIdHigh(MySQLDependenciesJob.java:233)
    at zipkin2.dependencies.mysql.MySQLDependenciesJob.run(MySQLDependenciesJob.java:184)
    at zipkin2.dependencies.ZipkinDependenciesJob.main(ZipkinDependenciesJob.java:65)
Caused by: java.sql.SQLInvalidAuthorizationSpecException: Access denied for user 'root'@'localhost' (using password: NO)
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.get(ExceptionMapper.java:173)
    at org.mariadb.jdbc.internal.util.exceptions.ExceptionMapper.getException(ExceptionMapper.java:110)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1115)
    at org.mariadb.jdbc.internal.util.Utils.retrieveProxy(Utils.java:502)
    at org.mariadb.jdbc.MariaDbConnection.newConnection(MariaDbConnection.java:154)
    at org.mariadb.jdbc.Driver.connect(Driver.java:86)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:247)
    at zipkin2.dependencies.mysql.MySQLDependenciesJob.hasTraceIdHigh(MySQLDependenciesJob.java:229)
    ... 2 more
Caused by: java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: NO)
Current charset is UTF-8. If password has been set using other charset, consider using option 'passwordCharacterEncoding'
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.authentication(AbstractConnectProtocol.java:862)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.handleConnectionPhases(AbstractConnectProtocol.java:785)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connect(AbstractConnectProtocol.java:456)
    at org.mariadb.jdbc.internal.protocol.AbstractConnectProtocol.connectWithoutProxy(AbstractConnectProtocol.java:1111)
    ... 8 more

追溯源码

逼不得已,走上查看源码之路,Idea打开zipkin-dependencies/mysql源码,查看相关部分代码。

public static final class Builder {
    Map<String, String> sparkProperties = ImmutableMap.of(
        "spark.ui.enabled", "false"
    );

    String db = getEnv("MYSQL_DB", "zipkin");
    String host = getEnv("MYSQL_HOST", "localhost");
    int port = Integer.parseInt(getEnv("MYSQL_TCP_PORT", "3306"));
    String user = getEnv("MYSQL_USER", "");
    String password = getEnv("MYSQL_PASS", "");
    int maxConnections = Integer.parseInt(getEnv("MYSQL_MAX_CONNECTIONS", "10"));
    boolean useSsl = Boolean.parseBoolean(getEnv("MYSQL_USE_SSL", "false"));

    // local[*] master lets us run & test the job locally without setting a Spark cluster
    String sparkMaster = getEnv("SPARK_MASTER", "local[*]");
    // By default the job only works on traces whose first timestamp is today
    long day = midnightUTC(System.currentTimeMillis());
    /** 
    *为减少篇幅,中间设置属性部分代码省略。
    */
    public MySQLDependenciesJob build() {
      return new MySQLDependenciesJob(this);
    }
  }
    /** 
    *为减少篇幅,中间部分代码省略。
    */
MySQLDependenciesJob(Builder builder) {
    this.db = builder.db;
    this.day = builder.day;
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));
    this.dateStamp = df.format(new Date(builder.day));
    this.url = new StringBuilder("jdbc:mysql://")
        .append(builder.host).append(":").append(builder.port)
        .append("/").append(builder.db)
        .append("?autoReconnect=true")
        .append("&useSSL=").append(builder.useSsl).toString();
    this.user = builder.user;
    this.password = builder.password;
    this.conf = new SparkConf(true)
        .setMaster(builder.sparkMaster)
        .setAppName(getClass().getName());
    if (builder.jars != null) conf.setJars(builder.jars);
    for (Map.Entry<String, String> entry : builder.sparkProperties.entrySet()) {
      conf.set(entry.getKey(), entry.getValue());
    }
    this.logInitializer = builder.logInitializer;
  }
  void saveToMySQL(List<DependencyLink> links) {
    try (Connection con = DriverManager.getConnection(url, user, password)) {
      PreparedStatement replace = con.prepareStatement(
              "REPLACE INTO zipkin_dependencies (day, parent, child, call_count, error_count) VALUES (?,?,?,?,?)");
      for (DependencyLink link : links) {
        replace.setDate(1, new java.sql.Date(day));
        replace.setString(2, link.parent());
        replace.setString(3, link.child());
        replace.setLong(4, link.callCount());
        replace.setLong(5, link.errorCount());
        replace.executeUpdate();
      }
    } catch (SQLException e) {
      throw new RuntimeException("Could not save links " + links, e);
    }
  }

然并卵,看完之后,没看出明显问题。难道还是我自己的mysql配置问题?还是启动部分的参数问题?代码部分也是有些疑惑,password和root为什么没放进url里,难道是为了安全考虑么?

 this.url = new StringBuilder("jdbc:mysql://")
        .append(builder.host).append(":").append(builder.port)
        .append("/").append(builder.db)
        .append("?autoReconnect=true")
        .append("&useSSL=").append(builder.useSsl).toString();
    this.user = builder.user;
    this.password = builder.password;

文中还提到 Current charset is UTF-8. If password has been set using other charset, consider using option 'passwordCharacterEncoding',编码格式是否有不同呢?
查看mysql数据库及表编码格式

mysql> show variables like 'character_set_database';
+------------------------+---------+
| Variable_name          | Value   |
+------------------------+---------+
| character_set_database | utf8mb4 |
+------------------------+---------+
1 row in set (0.01 sec)

参考链接

https://jira.mariadb.org/browse/CONJ-480
https://samebug.io/exceptions/2980875/java.sql.SQLInvalidAuthorizationSpecException/could-not-connect-access-denied-for-user
将请求参数作为UTF-8编码的字符串传递[重复]
zipkin
zipkin集成到node,C#
微服务之分布式跟踪系统(springboot+zipkin+mysql)
第二十九章 springboot + zipkin + mysql
Linux下的Mysql用命令执行sql文件
修改MySQL管理员密码
zipkin-server

总结

由于启动zipkin-dependencies链接mysql报Access denied for user 'root'@'localhost' (using password: NO)错误,本次持久化之路最终失败。但由于我直接使用【mysql -u 用户 -p】是能登录成功的,所以我猜测了以下原因:

  • 客户端自己的bug,和我服务器mysql版本不兼容?
  • 编码问题,编码两者不符?
  • 用户名和密码没有共享全局,只对一个数据库有效?

别人的博文是面向教学成功编程,我的是面向失败编程,也别有一番趣味。留下疑问,待日后解决调。虽然失败了,但我又收集了一堆链接,增添了mysql一些故障解决的认识。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容