java metric_java版的Metric工具介绍

Metrics是一个给JAVA服务的各项指标提供度量工具的包,在JAVA代码中嵌入Metrics代码,可以方便的对业务代码的各个指标进行监控,同时,Metrics能够很好的跟Ganlia、Graphite结合,方便的提供图形化接口。基本使用方式直接将core包(目前稳定版本3.0.1)导入pom文件即可,配置如下:

com.codahale.metricsgroupId> metrics-coreartifactId> 3.0.1version> dependency>

core包主要提供如下核心功能:

Metrics Registries类似一个metrics容器,维护一个Map,可以是一个服务一个实例。

支持五种metric类型:Gauges、Counters、Meters、Histograms和Timers。

可以将metrics值通过JMX、Console,CSV文件和SLF4J loggers发布出来。

五种Metrics类型:

1.       Gauges

Gauges是一个最简单的计量,一般用来统计瞬时状态的数据信息,比如系统中处于pending状态的job。测试代码

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.Gauge;

import com.codahale.metrics.JmxReporter;

import com.codahale.metrics.MetricRegistry;

import java.util.Queue;

import java.util.concurrent.LinkedBlockingDeque;

import java.util.concurrent.TimeUnit;

/**

* User: hzwangxx

* Date: 14-2-17

* Time: 14:47

* 测试Gauges,实时统计pending状态的job个数

*/

public class TestGauges {

/**

* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map

*/

private static final MetricRegistry metrics = new MetricRegistry();

private static Queue queue = new LinkedBlockingDeque();

/**

* 在控制台上打印输出

*/

private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

public static void main(String[] args) throws InterruptedException {

reporter.start(3, TimeUnit.SECONDS);

//实例化一个Gauge

Gauge gauge = new Gauge() {

@Override

public Integer getValue() {

return queue.size();

}

};

//注册到容器中

metrics.register(MetricRegistry.name(TestGauges.class, "pending-job", "size"), gauge);

//测试JMX

JmxReporter jmxReporter = JmxReporter.forRegistry(metrics).build();

jmxReporter.start();

//模拟数据

for (int i=0; i<20; i++){

queue.add("a");

Thread.sleep(1000);

}

}

}

/*

console output:

14-2-17 15:29:35 ===============================================================

-- Gauges ----------------------------------------------------------------------

com.netease.test.metrics.TestGauges.pending-job.size

value = 4

14-2-17 15:29:38 ===============================================================

-- Gauges ----------------------------------------------------------------------

com.netease.test.metrics.TestGauges.pending-job.size

value = 6

14-2-17 15:29:41 ===============================================================

-- Gauges ----------------------------------------------------------------------

com.netease.test.metrics.TestGauges.pending-job.size

value = 9

*/

通过以上步骤将会向MetricsRegistry容器中注册一个名字为com.netease.test.metrics .TestGauges.pending-job.size的metrics,实时获取队列长度的指标。另外,Core包种还扩展了几种特定的Gauge:

JMX Gauges—提供给第三方库只通过JMX将指标暴露出来。

Ratio Gauges—简单地通过创建一个gauge计算两个数的比值。

Cached Gauges—对某些计量指标提供缓存

Derivative Gauges—提供Gauge的值是基于其他Gauge值的接口。

2.       Counter

Counter是Gauge的一个特例,维护一个计数器,可以通过inc()和dec()方法对计数器做修改。使用步骤与Gauge基本类似,在MetricRegistry中提供了静态方法可以直接实例化一个Counter。

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.Counter;

import com.codahale.metrics.MetricRegistry;

import java.util.LinkedList;

import java.util.Queue;

import java.util.concurrent.TimeUnit;

import static com.codahale.metrics.MetricRegistry.*;

/**

* User: hzwangxx

* Date: 14-2-14

* Time: 14:02

* 测试Counter

*/

public class TestCounter {

/**

* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map

*/

private static final MetricRegistry metrics = new MetricRegistry();

/**

* 在控制台上打印输出

*/

private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

/**

* 实例化一个counter,同样可以通过如下方式进行实例化再注册进去

* pendingJobs = new Counter();

* metrics.register(MetricRegistry.name(TestCounter.class, "pending-jobs"), pendingJobs);

*/

private static Counter pendingJobs = metrics.counter(name(TestCounter.class, "pedding-jobs"));

// private static Counter pendingJobs = metrics.counter(MetricRegistry.name(TestCounter.class, "pedding-jobs"));

private static Queue queue = new LinkedList();

public static void add(String str) {

pendingJobs.inc();

queue.offer(str);

}

public String take() {

pendingJobs.dec();

return queue.poll();

}

public static void main(String[]args) throws InterruptedException {

reporter.start(3, TimeUnit.SECONDS);

while(true){

add("1");

Thread.sleep(1000);

}

}

}

/*

console output:

14-2-17 17:52:34 ===============================================================

-- Counters --------------------------------------------------------------------

com.netease.test.metrics.TestCounter.pedding-jobs

count = 4

14-2-17 17:52:37 ===============================================================

-- Counters --------------------------------------------------------------------

com.netease.test.metrics.TestCounter.pedding-jobs

count = 6

14-2-17 17:52:40 ===============================================================

-- Counters --------------------------------------------------------------------

com.netease.test.metrics.TestCounter.pedding-jobs

count = 9

*/

3.       Meters

Meters用来度量某个时间段的平均处理次数(request per second),每1、5、15分钟的TPS。比如一个service的请求数,通过metrics.meter()实例化一个Meter之后,然后通过meter.mark()方法就能将本次请求记录下来。统计结果有总的请求数,平均每秒的请求数,以及最近的1、5、15分钟的平均TPS。

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.Meter;

import com.codahale.metrics.MetricRegistry;

import java.util.concurrent.TimeUnit;

import static com.codahale.metrics.MetricRegistry.*;

/**

* User: hzwangxx

* Date: 14-2-17

* Time: 18:34

* 测试Meters

*/

public class TestMeters {

/**

* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map

*/

private static final MetricRegistry metrics = new MetricRegistry();

/**

* 在控制台上打印输出

*/

private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

/**

* 实例化一个Meter

*/

private static final Meter requests = metrics.meter(name(TestMeters.class, "request"));

public static void handleRequest() {

requests.mark();

}

public static void main(String[] args) throws InterruptedException {

reporter.start(3, TimeUnit.SECONDS);

while(true){

handleRequest();

Thread.sleep(100);

}

}

}

/*

14-2-17 18:43:08 ===============================================================

-- Meters ----------------------------------------------------------------------

com.netease.test.metrics.TestMeters.request

count = 30

mean rate = 9.95 events/second

1-minute rate = 0.00 events/second

5-minute rate = 0.00 events/second

15-minute rate = 0.00 events/second

14-2-17 18:43:11 ===============================================================

-- Meters ----------------------------------------------------------------------

com.netease.test.metrics.TestMeters.request

count = 60

mean rate = 9.99 events/second

1-minute rate = 10.00 events/second

5-minute rate = 10.00 events/second

15-minute rate = 10.00 events/second

14-2-17 18:43:14 ===============================================================

-- Meters ----------------------------------------------------------------------

com.netease.test.metrics.TestMeters.request

count = 90

mean rate = 9.99 events/second

1-minute rate = 10.00 events/second

5-minute rate = 10.00 events/second

15-minute rate = 10.00 events/second

*/

4.       Histograms

Histograms主要使用来统计数据的分布情况,最大值、最小值、平均值、中位数,百分比(75%、90%、95%、98%、99%和99.9%)。例如,需要统计某个页面的请求响应时间分布情况,可以使用该种类型的Metrics进行统计。具体的样例代码如下:

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.Histogram;

import com.codahale.metrics.MetricRegistry;

import java.util.Random;

import java.util.concurrent.TimeUnit;

import static com.codahale.metrics.MetricRegistry.name;

/**

* User: hzwangxx

* Date: 14-2-17

* Time: 18:34

* 测试Histograms

*/

public class TestHistograms {

/**

* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map

*/

private static final MetricRegistry metrics = new MetricRegistry();

/**

* 在控制台上打印输出

*/

private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

/**

* 实例化一个Histograms

*/

private static final Histogram randomNums = metrics.histogram(name(TestHistograms.class, "random"));

public static void handleRequest(double random) {

randomNums.update((int) (random*100));

}

public static void main(String[] args) throws InterruptedException {

reporter.start(3, TimeUnit.SECONDS);

Random rand = new Random();

while(true){

handleRequest(rand.nextDouble());

Thread.sleep(100);

}

}

}

/*

14-2-17 19:39:11 ===============================================================

-- Histograms ------------------------------------------------------------------

com.netease.test.metrics.TestHistograms.random

count = 30

min = 1

max = 97

mean = 45.93

stddev = 29.12

median = 39.50

75% <= 71.00

95% <= 95.90

98% <= 97.00

99% <= 97.00

99.9% <= 97.00

14-2-17 19:39:14 ===============================================================

-- Histograms ------------------------------------------------------------------

com.netease.test.metrics.TestHistograms.random

count = 60

min = 0

max = 97

mean = 41.17

stddev = 28.60

median = 34.50

75% <= 69.75

95% <= 92.90

98% <= 96.56

99% <= 97.00

99.9% <= 97.00

14-2-17 19:39:17 ===============================================================

-- Histograms ------------------------------------------------------------------

com.netease.test.metrics.TestHistograms.random

count = 90

min = 0

max = 97

mean = 44.67

stddev = 28.47

median = 43.00

75% <= 71.00

95% <= 91.90

98% <= 96.18

99% <= 97.00

99.9% <= 97.00

*/

5.       Timers

Timers主要是用来统计某一块代码段的执行时间以及其分布情况,具体是基于Histograms和Meters来实现的。样例代码如下:

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.ConsoleReporter;

import com.codahale.metrics.MetricRegistry;

import com.codahale.metrics.Timer;

import java.util.Random;

import java.util.concurrent.TimeUnit;

import static com.codahale.metrics.MetricRegistry.name;

/**

* User: hzwangxx

* Date: 14-2-17

* Time: 18:34

* 测试Timers

*/

public class TestTimers {

/**

* 实例化一个registry,最核心的一个模块,相当于一个应用程序的metrics系统的容器,维护一个Map

*/

private static final MetricRegistry metrics = new MetricRegistry();

/**

* 在控制台上打印输出

*/

private static ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

/**

* 实例化一个Meter

*/

// private static final Timer requests = metrics.timer(name(TestTimers.class, "request"));

private static final Timer requests = metrics.timer(name(TestTimers.class, "request"));

public static void handleRequest(int sleep) {

Timer.Context context = requests.time();

try {

//some operator

Thread.sleep(sleep);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

context.stop();

}

}

public static void main(String[] args) throws InterruptedException {

reporter.start(3, TimeUnit.SECONDS);

Random random = new Random();

while(true){

handleRequest(random.nextInt(1000));

}

}

}

/*

14-2-18 9:31:54 ================================================================

-- Timers ----------------------------------------------------------------------

com.netease.test.metrics.TestTimers.request

count = 4

mean rate = 1.33 calls/second

1-minute rate = 0.00 calls/second

5-minute rate = 0.00 calls/second

15-minute rate = 0.00 calls/second

min = 483.07 milliseconds

max = 901.92 milliseconds

mean = 612.64 milliseconds

stddev = 196.32 milliseconds

median = 532.79 milliseconds

75% <= 818.31 milliseconds

95% <= 901.92 milliseconds

98% <= 901.92 milliseconds

99% <= 901.92 milliseconds

99.9% <= 901.92 milliseconds

14-2-18 9:31:57 ================================================================

-- Timers ----------------------------------------------------------------------

com.netease.test.metrics.TestTimers.request

count = 8

mean rate = 1.33 calls/second

1-minute rate = 1.40 calls/second

5-minute rate = 1.40 calls/second

15-minute rate = 1.40 calls/second

min = 41.07 milliseconds

max = 968.19 milliseconds

mean = 639.50 milliseconds

stddev = 306.12 milliseconds

median = 692.77 milliseconds

75% <= 885.96 milliseconds

95% <= 968.19 milliseconds

98% <= 968.19 milliseconds

99% <= 968.19 milliseconds

99.9% <= 968.19 milliseconds

14-2-18 9:32:00 ================================================================

-- Timers ----------------------------------------------------------------------

com.netease.test.metrics.TestTimers.request

count = 15

mean rate = 1.67 calls/second

1-minute rate = 1.40 calls/second

5-minute rate = 1.40 calls/second

15-minute rate = 1.40 calls/second

min = 41.07 milliseconds

max = 968.19 milliseconds

mean = 591.35 milliseconds

stddev = 302.96 milliseconds

median = 650.56 milliseconds

75% <= 838.07 milliseconds

95% <= 968.19 milliseconds

98% <= 968.19 milliseconds

99% <= 968.19 milliseconds

99.9% <= 968.19 milliseconds

*/

6  Health Checks

Metrics提供了一个独立的模块:Health Checks,用于对Application、其子模块或者关联模块的运行是否正常做检测。该模块是独立metrics-core模块的,使用时则导入metrics-healthchecks包。

com.codahale.metricsgroupId> metrics-healthchecksartifactId> 3.0.1version> dependency>

使用起来和与上述几种类型的Metrics有点类似,但是需要重新实例化一个Metrics容器HealthCheckRegistry,待检测模块继承抽象类HealthCheck并实现check()方法即可,然后将该模块注册到HealthCheckRegistry中,判断的时候通过isHealthy()接口即可。如下示例代码:

点击(此处)折叠或打开

package com.netease.test.metrics;

import com.codahale.metrics.health.HealthCheck;

import com.codahale.metrics.health.HealthCheckRegistry;

import java.util.Map;

import java.util.Random;

/**

* User: hzwangxx

* Date: 14-2-18

* Time: 9:57

*/

public class DatabaseHealthCheck extends HealthCheck{

private final Database database;

public DatabaseHealthCheck(Database database) {

this.database = database;

}

@Override

protected Result check() throws Exception {

if (database.ping()) {

return Result.healthy();

}

return Result.unhealthy("Can't ping database.");

}

/**

* 模拟Database对象

*/

static class Database {

/**

* 模拟database的ping方法

* @return 随机返回boolean值

*/

public boolean ping() {

Random random = new Random();

return random.nextBoolean();

}

}

public static void main(String[] args) {

// MetricRegistry metrics = new MetricRegistry();

// ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics).build();

HealthCheckRegistry registry = new HealthCheckRegistry();

registry.register("database1", new DatabaseHealthCheck(new Database()));

registry.register("database2", new DatabaseHealthCheck(new Database()));

while (true) {

for (Map.Entry entry : registry.runHealthChecks().entrySet()) {

if (entry.getValue().isHealthy()) {

System.out.println(entry.getKey() + ": OK");

} else {

System.err.println(entry.getKey() + ": FAIL, error message: " + entry.getValue().getMessage());

final Throwable e = entry.getValue().getError();

if (e != null) {

e.printStackTrace();

}

}

}

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

}

}

}

}

/*

console output:

database1: OK

database2: FAIL, error message: Can't ping database.

database1: FAIL, error message: Can't ping database.

database2: OK

database1: OK

database2: FAIL, error message: Can't ping database.

database1: FAIL, error message: Can't ping database.

database2: OK

database1: FAIL, error message: Can't ping database.

database2: FAIL, error message: Can't ping database.

database1: FAIL, error message: Can't ping database.

database2: FAIL, error message: Can't ping database.

database1: OK

database2: OK

database1: OK

database2: FAIL, error message: Can't ping database.

database1: FAIL, error message: Can't ping database.

database2: OK

database1: OK

database2: OK

database1: FAIL, error message: Can't ping database.

database2: OK

database1: OK

database2: OK

database1: OK

database2: OK

database1: OK

database2: FAIL, error message: Can't ping database.

database1: FAIL, error message: Can't ping database.

database2: FAIL, error message: Can't ping database.

*/

其他支持

metrics提供了对Ehcache、Apache HttpClient、JDBI、Jersey、Jetty、Log4J、Logback、JVM等的集成,可以方便地将Metrics输出到Ganglia、Graphite中,供用户图形化展示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/459811.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15_采用Pull解析器解析和生成XML内容

java还提供SAX和DOM用于解析XML Android还集成了Pull解析器——推荐 package cn.itcast.service;import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1…

Android自定义view之圆形进度条

本节介绍自定义view-圆形进度条思路&#xff1a;根据前面介绍的自定义view内容可拓展得之&#xff1b;1&#xff1a;新建类继承自View2&#xff1a;添加自定义view属性3&#xff1a;重写onDraw(Canvas canvas)4&#xff1a;实现功能下面上代码1.自定义view代码&#xff1a; pub…

java二级考试备考_2017计算机二级考试《JAVA》备考测试题「带答案」

2017计算机二级考试《JAVA》备考测试题「带答案」为确保同学们将所涉及的考点全面复习到位&#xff0c;让大家充满信心的步入考场&#xff0c;以下是百分网小编搜索整理的一份计算机二级考试《JAVA》备考测试题【带答案】&#xff0c;供参考练习&#xff0c;希望对大家有所帮助…

Thinkphp 关联模型和试图模型区别

关联模型主要在多表操作时使用&#xff0c;比如 user表&#xff0c;user_role表&#xff0c;role表 user_role字段&#xff1a;uid,rid&#xff0c;它作为中间表&#xff0c;负责将user和role之间的&#xff0c;1对1&#xff0c;1对多&#xff0c;多对多的关系进行保存。 这时要…

windows7下安装php的imagick和imagemagick扩展教程

这篇文章主要介绍了windows7下安装php的imagick和imagemagick扩展教程,同样也适应XP操作系统,Win8下就没测试过了,需要的朋友可以参考下 最近的PHP项目中&#xff0c;需要用到切图和缩图的效果&#xff0c;在linux测试服务器上很轻松的就安装好php imagick扩展。但是在本地wind…

java 线程间通信 handler_Handler不同线程间的通信

转http://www.iteye.com/problems/69457Activity启动后点击一个界面按钮后会开启一个服务(暂定为padService)&#xff0c;在padService中会启动一个线程(暂定为Thread-3)发起Socket连接。我们项目中使用mina作为socket通信框架&#xff0c;用过mina的同志们应该熟悉&#xff0c…

通过mysql show processlist 命令检查mysql锁的方法

作者&#xff1a; 字体&#xff1a;[增加 减小] 类型&#xff1a;转载 时间&#xff1a;2010-03-07show processlist 命令非常实用&#xff0c;有时候mysql经常跑到50%以上或更多&#xff0c;就需要用这个命令看哪个sql语句占用资源比较多&#xff0c;就知道哪个网站的程序问题…

java流类图结构_java学习之IO流(学习之旅,一)

个人在学习IO流的时候看到如下所示java 流类图结构的时候&#xff0c;我的感想是&#xff0c;这么多处于蒙的状态。Java流类图结构这么多&#xff0c;没有分类不好学&#xff0c;那我们就慢慢一口一口的吃&#xff0c;这样每天学习一点就好了&#xff0c;其实很多类并不是常用的…

php 安装xdebug扩展

php 扩展获取地址 http://pecl.php.net/package/ 编译安装的过程 wget http://pecl.php.net/get/xdebug-2.2.2.tgz tar -zxvf xdebug-2.2.2.tgz cd xdebug-2.2.2/ /data/klj/php/bin/phpize ./configure --enable-xdebug --with-php-config/data/klj/php/bin/php-config mak…

通过VB向SQL Server数据库中录入数据

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)一、数据录入通过VB向SQL Server数据库中录入数据&#xff0c;可以使用数据绑定控件录入数据与使用SQL语句录入1.利用数据绑定控件录入数据使用数据绑定控件录入数据可以运行较少的代码&…

拨打电话 java_简单拨打电话程序

众所周知,对于一个手机,能拨打电话是其最重要也是最常用的一个功能.而在Android里是怎么样实现拨打电话的程序呢?我在这里写了一个简单的拨打电话的Demo,供大家参考.一共分为5个步骤.Step 1:新建一个Android工程,命名为phoneCallDemo.Step 2:设计程序的界面,打开main.xml把内容…

Apple开发者账号申请学习方式

http://jingyan.baidu.com/article/414eccf610e7c76b431f0a94.html https://developer.apple.com/wwdc/schedule/转载于:https://www.cnblogs.com/wcLT/p/4167707.html

SQLite/嵌入式数据库

SQLite/嵌入式数据库 的项目要么不使用数据库&#xff08;一两个文配置文件就可以搞定&#xff09;&#xff0c;要么就会有很多的数据&#xff0c;用到 postgresql&#xff0c;操练sqlite的还没有。现在我有个自己的小测试例子&#xff0c;写个数据库对比的小项目例子&#xff…

python继承属性_Python中的属性继承问题

不久前&#xff0c;我在开发一个python应用程序&#xff0c;我在类中使用了很多属性&#xff0c;但是当我试图重写派生类中基类的访问器的行为时&#xff0c;我遇到了麻烦。这是我的问题的草图&#xff1a;class Person(object):propertydef name(self):return self._namename.…

王爽汇编语言实验十

实验十 3.数值显示(以下程序附带测试程序) 1 ;名称: dtoc2 ;功能: 将dword型数据转变为表示十进制数的字符串,字符串以0为结尾3 ;参数: (ax)dword型数据低字4 ; (dx)dword型数据高字5 ; ds:si指向字符串的首地址6 ;返回: 无7 assume cs:code8 data segment9…

WPF01(xaml)

XAML&#xff1a;&#xff08;转自http://www.cnblogs.com/huangxincheng/archive/2012/06/17/2552511.html&#xff09; <Window x:Class"WpfApplication1.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"…

android源码包下载

http://rgruet.free.fr/public/其他下载地址&#xff1a;http://cid-b50f9d5897331c44.office.live.com/browse.aspx/Android技术群共享/source code 转载于:https://www.cnblogs.com/liangxiaofeng/p/4173340.html

java 线程 状态 图_Java提高——多线程(一)状态图

操作系统中的进程和线程的概念进程是指一个内存运行的应用程序&#xff0c;每个进程都有自己独立的一块内存空间&#xff0c;一个进程中可以启动多个线程&#xff0c;比如windows下的一个运行的应用程序.exe就是一个进程。线程是指进程中的一个执行流&#xff0c;一个进程可以运…

UITableView 重用cell方法edequeueReusableCellWithIdentifier,出现错误

UITableView 使用重用cell方法edequeueReusableCellWithIdentifier&#xff0c;出现错误&#xff1a;*** Terminating app due to uncaught exception NSInternalInconsistencyException, reason: unable to dequeue a cell with identifier cell3 - must register a nib or a …

学习ecshop 教程网址

http://www.chinab4c.com&#xff08;中国B4C电子商务&#xff09;转载于:https://www.cnblogs.com/ymj0906/p/4175681.html