{"id":50878340,"url":"https://github.com/ChanningBJ/meerkat","last_synced_at":"2026-07-03T02:01:14.030Z","repository":{"id":57722263,"uuid":"92911480","full_name":"ChanningBJ/meerkat","owner":"ChanningBJ","description":"meerkat 是用于服务监控以及服务降级基础组件，主要为了解决调用外部接口的时候进行成功率，响应时间，QPS指标的监控，同时在成功率下降到预设的阈值以下的时候自动切断外部接口的调用，外部接口成功率恢复后自动恢复请求","archived":false,"fork":false,"pushed_at":"2017-08-31T13:27:18.000Z","size":401,"stargazers_count":51,"open_issues_count":0,"forks_count":21,"subscribers_count":9,"default_branch":"master","last_synced_at":"2026-02-27T05:34:53.303Z","etag":null,"topics":["fusing-data","java","monitoring"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChanningBJ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-31T06:16:34.000Z","updated_at":"2026-01-27T09:00:52.000Z","dependencies_parsed_at":"2022-08-29T22:32:21.507Z","dependency_job_id":null,"html_url":"https://github.com/ChanningBJ/meerkat","commit_stats":null,"previous_names":["qiyimbd/meerkat"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ChanningBJ/meerkat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChanningBJ%2Fmeerkat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChanningBJ%2Fmeerkat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChanningBJ%2Fmeerkat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChanningBJ%2Fmeerkat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChanningBJ","download_url":"https://codeload.github.com/ChanningBJ/meerkat/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChanningBJ%2Fmeerkat/sbom","scorecard":{"id":28526,"data":{"date":"2025-08-11","repo":{"name":"github.com/ChanningBJ/meerkat","commit":"5547c989a35bb2c592b3571318b8f926e79a3949"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.4,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 1/21 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":0,"reason":"license file not detected","details":["Warn: project does not have a license file"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 12 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":1,"reason":"9 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-5mg8-w23w-74h3","Warn: Project is vulnerable to: GHSA-7g45-4rm6-3mm3","Warn: Project is vulnerable to: GHSA-mvr2-9pj6-7w5j","Warn: Project is vulnerable to: GHSA-2qrg-x229-3v8q","Warn: Project is vulnerable to: GHSA-65fg-84f6-3jq3","Warn: Project is vulnerable to: GHSA-f7vh-qwp3-x37m","Warn: Project is vulnerable to: GHSA-fp5r-v3w9-4333","Warn: Project is vulnerable to: GHSA-w9p3-5cr8-m3jj","Warn: Project is vulnerable to: GHSA-j288-q9x7-2f5v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T18:29:07.353Z","repository_id":57722263,"created_at":"2025-08-14T18:29:07.353Z","updated_at":"2025-08-14T18:29:07.353Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35069183,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-03T02:00:05.635Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fusing-data","java","monitoring"],"created_at":"2026-06-15T12:00:25.786Z","updated_at":"2026-07-03T02:01:14.024Z","avatar_url":"https://github.com/ChanningBJ.png","language":"Java","funding_links":[],"categories":["容错组件"],"sub_categories":["微服务框架"],"readme":"# meerkat\n\nmeerkat 是用于服务监控以及服务降级基础组件，主要为了解决调用外部接口的时候进行成功率，响应时间，QPS指标的监控，同时在成功率下降到预设的阈值以下的时候自动切断外部接口的调用，外部接口成功率恢复后自动恢复请求。\n\n## 主要功能\n\n1. 监控操作的成功率以及响应时间指标\n1. log文件和Grafhite两种监控指标上报方式，支持扩展其他的上报方式\n1. （可选功能）成功率下降到预设的阈值以下触发熔断保护，暂定对外部接口的访问，成功率恢复以后自动恢复访问\n\n更详细的文档说明请参见 [Wiki](https://github.com/ChanningBJ/meerkat/wiki)\n\n通过日志的方式查看监控结果\n```\ntype=GAUGE, name=com.test.GetPlayCountCommand.normal-rate, value=0.0\ntype=GAUGE, name=com.test.GetPlayCountCommand.success-rate, value=61.0\ntype=TIMER, name=com.test.GetPlayCountCommand.time, count=25866500, min=0.0, max=0.001, mean=3.963926781047921E-5, stddev=1.951102156677818E-4, median=0.0, p75=0.0, p95=0.0, p98=0.001, p99=0.001, p999=0.001, mean_rate=649806.0831335272, m1=1665370.7316699813, m5=2315813.300713087, m15=2446572.324069477, rate_unit=events/second, duration_unit=milliseconds\n```\n\n通过上报Grafana查看监控结果\n![image](https://github.com/ChanningBJ/meerkat/blob/master/dashboard.png)\n\n\n## Maven \n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.github.channingbj\u003c/groupId\u003e\n    \u003cartifactId\u003emeerkat\u003c/artifactId\u003e\n    \u003cversion\u003e1.2\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n\n\n## 如何使用\n\n### 基本使用\n\n假设我们的服务中需要从HTTP接口查询一个节目的播放次数，为了防止这个HTTP接口大量超时影响我们自身服务的质量，可以定义一个查询Command：\n\n```java\npublic class GetPlayCountCommand extends FusingCommand\u003cLong\u003e {\n\n    private final Long videoID;\n\n    public GetPlayCountCommand(Long videoID) {\n        this.videoID = videoID;\n    }\n        \n    protected Optional\u003cLong\u003e run() {\n        Long result = 0l;\n        // 调用HTTP接口获取视频的播放次数信息\n        // 如果调用失败，返回 null 或者抛出异常，会将这次操作记录为失败\n        // 如果ID非法，返回 Optional.absent(),会将这次操作记录为成功\n        return Optional.fromNullable(result);\n    }\n}\n```  \n\n执行查询：\n\n```java\n//获取视频ID为123的视频的播放次数\nGetPlayCountCommand command = new GetPlayCountCommand(123l);\nLong result = command.execute(); // 执行查询操作，如果执行失败或者处于熔断状态，返回 null \n```\n\n默认情况下，操作成功率每10秒更新一次，开启通断以后当成功率小于90%的时候进入熔断状态，熔断状态下调用execute()函数会返回null，熔断状态持续50秒，终端终止以后恢复正常访问。\n\n### 定义熔断状态下的返回结果\n\n当处于熔断或者命令执行失败的时候execute()函数会返回null，可以通过Override getFallback这个函数对失败／熔断情况下的返回值进行修改。例如我们希望在播放次数请求失败的情况下默认播放次数是47，可以这样实现：\n\n```java\npublic class GetPlayCountCommand extends FusingCommand\u003cLong\u003e {\n\n    private final Long videoID;\n\n    public GetPlayCountCommand(Long videoID) {\n        this.videoID = videoID;\n    }\n        \n    protected Optional\u003cLong\u003e run() {\n        Long result = 0l;\n        // 调用HTTP接口获取视频的播放次数信息\n        // 如果调用失败，返回 null 或者抛出异常，会将这次操作记录为失败\n        // 如果ID非法，返回 Optional.absent(),会将这次操作记录为成功\n        return Optional.fromNullable(result);\n    }\n    \n    /**\n     *\n     * @param isFusing 当前是否处于熔断状态\n     * @param e        run函数抛出的异常\n     * @return 熔断或者请求出现错误的时候execute()函数的返回值\n     */\n    @Override\n    protected Long getFallback(boolean isFusing, Exception e) {\n        return 47l;\n    }\n}\n```  \n\n\n\n\n\n\n\n\n### 配置监控上报\n\n\n#### 定义配置文件\n\n首先定义一个接口，继承自GraphiteReporterConfig，通过这个接口定义配置文件的加载路径。配置文件路径的定义方法请参照 [owner 文档](http://owner.aeonbits.org), 下面是一个例子：\n\n\n```java\n@Config.Sources(\"classpath:config.properties\")\npublic interface MyConfig extends GraphiteReporterConfig {\n}\n```\n\n\n配置文件中需要定义下列内容：\n\n\n配置项 | 含义\n------------ | -------------\nmeter.reporter.enabled.hosts | 开启监控上报的服务器列表\nmeter.reporter.perfix | 上报使用的前缀\nmeter.reporter.carbon.host | grafana(carbon-cache) 的 IP 地址，用于存储监控数据\nmeter.reporter.carbon.port| grafana(carbon-cache) 的端口\n\n下面这个例子是在192.168.0.0.1和192.168.0.0.2两台服务器上开启监控数据上报，上报监控指标的前缀是project_name.dc：\n```\nmeter.reporter.enabled.hosts = 192.168.0.0.1,192.168.0.0.2\nmeter.reporter.perfix = project_name.dc\nmeter.reporter.carbon.host = hostname.graphite\n```\n由于相同机房的不同服务器对外部接口的访问情况一般比较类似，所以仅选取部分机器上报，也是为了节省资源。仅选择部分机器上报不影响熔断效果。\n\n\n#### 初始化配置上报\n\n在服务初始化的时候需要对监控上报进行设置。下面的例子中开启了监控数据向日志文件的打印，同时通过MyConfig指定的配置文件加载Graphite配置信息。\n\n```\nMeterCenter.INSTANCE\n    .enableReporter(new EnablingLogReporter(\"org.apache.log4j.RollingFileAppender\"))\n    .enableReporter(new EnablingGraphiteReporter(MyConfig.class))\n    .init();\n```\n\n\n#### 查看统计结果\n\n\n统计结果会以熔断命令类名为进行分组。例如前面我们定义的 GetPlayCountCommand 类,package name 是 com.test，那么在日志中的输出将会是这个样子：\n\n```\ntype=GAUGE, name=com.test.GetPlayCountCommand.normal-rate, value=0.0\ntype=GAUGE, name=com.test.GetPlayCountCommand.success-rate, value=61.0\ntype=TIMER, name=com.test.GetPlayCountCommand.time, count=25866500, min=0.0, max=0.001, mean=3.963926781047921E-5, stddev=1.951102156677818E-4, median=0.0, p75=0.0, p95=0.0, p98=0.001, p99=0.001, p999=0.001, mean_rate=649806.0831335272, m1=1665370.7316699813, m5=2315813.300713087, m15=2446572.324069477, rate_unit=events/second, duration_unit=milliseconds\n```\n\n监控项 | 含义\n------------ | -------------\n[classname].success-rate | 成功率\n[classname].time.m1 | QPS\n[classname].time.mean | 平均响应时间\n[classname].normal-rate | 过去1分钟内处于正常访问（非熔断）的时间比例\n\n\n如果配置了Graphite上报，可以看到下面的监控图\n\n\n![image](https://github.com/ChanningBJ/meerkat/blob/master/dashboard.png)\n \n关于Graphite+Grafana的配置，可以参考文章：[使用graphite和grafana进行应用程序监控](https://segmentfault.com/a/1190000007540752) \n \n### 配置熔断的阀值和持续时间\n\n首先创建一个接口，继承自FusingConfig，用于指定配置文件的加载路径，同时还可以设定配置文件的刷新时间，具体定义方法请参照 [owner 文档](http://owner.aeonbits.org)\n\n```java\n@Config.Sources(\"classpath:app_config.properties\")\n@Config.HotReload(\n        value = 1, unit = java.util.concurrent.TimeUnit.MINUTES,\n        type = Config.HotReloadType.ASYNC)\npublic interface APPFusingConfig extends FusingConfig {\n}\n```\n\n创建查询Command的时候在构造函数中传入\n\n```java\npublic class GetPlayCountCommand extends FusingCommand\u003cLong\u003e {\n\n    private final Long videoID;\n\n    public GetPlayCountCommand(Long videoID) {\n        super( APPFusingConfig.class);\n        this.videoID = videoID;\n    }\n        \n    protected Optional\u003cLong\u003e run() {\n        Long result = 0l;\n        // 调用HTTP接口获取视频的播放次数信息\n        // 如果调用失败，返回 null 或者抛出异常，会将这次操作记录为失败\n        // 如果ID非法，返回 Optional.absent(),会将这次操作记录为成功\n        return Optional.fromNullable(result);\n    }\n}\n``` \n\n\n配置文件定义：\n\n\n监控项 | 含义 | 默认值\n------------ | ------------- | -------------\nfusing.[CommandClassName].mode | 熔断模式：\u003cbr\u003eFORCE_NORMAL－关闭熔断功能;\u003cbr\u003e AUTO_FUSING－自动进入熔断模式;\u003cbr\u003e FORCE_NORMAL－强制进行熔断 | FORCE_NORMAL\nfusing.[CommandClassName].duration | 触发一次熔断以后持续的时间，支持ms,sec,min 单位。例如 10sec | 50sec\nfusing.[CommandClassName].success_rate_threshold | 触发熔断的成功率阀值，降低到这个成功率以下将触发熔断，例如0.9表示成功率90% | 0.9\n\n配置文件中的 CommandClassName 是每个操作类的名称，可以为每个操作单独设置上述参数。同时，这个配置文件支持动态加载，乐意通过修改fusing.[CommandClassName].mode 手工触发或者关闭熔断。\n\n\n使用中有任何问题，欢迎与我联系\n\n![image](https://github.com/ChanningBJ/meerkat/blob/master/wechat.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChanningBJ%2Fmeerkat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FChanningBJ%2Fmeerkat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChanningBJ%2Fmeerkat/lists"}