├── .gitignore
├── LICENSE
├── README.md
├── dubbo
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── dubbo
│ │ ├── api
│ │ └── DemoService.java
│ │ ├── client
│ │ └── Consumer.java
│ │ └── service
│ │ ├── DemoServiceImpl.java
│ │ └── Server.java
│ └── resources
│ ├── META-INF
│ └── app.properties
│ ├── consumer.xml
│ ├── log4j2.xml
│ └── server.xml
├── dynamic-datasource
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── dynamic
│ │ └── datasource
│ │ ├── Application.java
│ │ ├── RefreshableDataSourceConfiguration.java
│ │ ├── ds
│ │ └── DynamicDataSource.java
│ │ ├── entity
│ │ └── User.java
│ │ ├── repository
│ │ └── UserRepository.java
│ │ └── util
│ │ ├── CustomizedConfigurationPropertiesBinder.java
│ │ ├── DataSourceManager.java
│ │ ├── DataSourceRefresher.java
│ │ └── DataSourceTerminationTask.java
│ └── resources
│ ├── application.properties
│ └── sql
│ ├── test1.sql
│ └── test2.sql
├── netflix-archaius
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── netflix
│ │ └── archaius
│ │ └── Application.java
│ └── resources
│ └── log4j2.xml
├── pom.xml
├── properties-keeper
├── README.md
├── fetch_properties.sh
└── pom.xml
├── sentinel
├── README.md
└── pom.xml
├── spring-boot-agent
├── README.md
├── apollo-agent
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── agent
│ │ └── ApolloAgent.java
├── app
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com.ctrip.framevowrk.apollo.use.cases.agent
│ │ │ └── Application.java
│ │ └── resources
│ │ └── application.properties
└── pom.xml
├── spring-boot-dubbo
├── README.md
├── pom.xml
├── spring-boot-dubbo-api
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ └── java
│ │ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── boot
│ │ └── starter
│ │ └── dubbo
│ │ └── api
│ │ └── DemoService.java
├── spring-boot-dubbo-consumer
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── com
│ │ │ └── ctrip
│ │ │ └── framework
│ │ │ └── apollo
│ │ │ └── use
│ │ │ └── cases
│ │ │ └── spring
│ │ │ └── boot
│ │ │ └── starter
│ │ │ └── dubbo
│ │ │ └── consumer
│ │ │ └── Consumer.java
│ │ └── resources
│ │ ├── application.properties
│ │ └── log4j2.xml
└── spring-boot-dubbo-provider
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── boot
│ │ └── starter
│ │ └── dubbo
│ │ └── provider
│ │ ├── DemoServiceImpl.java
│ │ └── Server.java
│ └── resources
│ ├── application.properties
│ └── logback-spring.xml
├── spring-boot-encrypt
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── boot
│ │ └── encrypt
│ │ ├── Application.java
│ │ └── EncryptUtil.java
│ └── resources
│ └── application.properties
├── spring-boot-logger
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── boot
│ │ └── logger
│ │ ├── Application.java
│ │ ├── LoggerConfiguration.java
│ │ └── PrintLogger.java
│ └── resources
│ └── application.properties
├── spring-cloud-gateway
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── cloud
│ │ └── gateway
│ │ ├── GatewayPropertiesRefresher.java
│ │ └── SpringCloudGatewayApplication.java
│ └── resources
│ └── application.properties
├── spring-cloud-logger
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── cloud
│ │ └── logger
│ │ ├── Application.java
│ │ ├── LoggerLevelRefresher.java
│ │ └── PrintLogger.java
│ └── resources
│ └── application.properties
├── spring-cloud-zuul-ratelimit
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── cloud
│ │ └── zuul
│ │ ├── Application.java
│ │ └── ZuulRateLimitPropertiesRefreshConfig.java
│ └── resources
│ ├── application.properties
│ └── log4j2.xml
├── spring-cloud-zuul
├── README.md
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── ctrip
│ │ └── framework
│ │ └── apollo
│ │ └── use
│ │ └── cases
│ │ └── spring
│ │ └── cloud
│ │ └── zuul
│ │ ├── Application.java
│ │ └── ZuulPropertiesRefresher.java
│ └── resources
│ ├── application.properties
│ └── log4j2.xml
└── spring-mvc-logger
├── README.md
├── pom.xml
└── src
└── main
├── java
└── com
│ └── ctrip
│ └── framework
│ └── apollo
│ └── use
│ └── cases
│ └── spring
│ └── mvc
│ └── logger
│ └── LoggerStartupListener.java
├── resources
├── META-INF
│ └── app.properties
├── logback.xml
└── spring-web.xml
└── webapp
└── WEB-INF
└── web.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 | .DS_Store
3 | application.pid
4 |
5 | # Mobile Tools for Java (J2ME)
6 | .mtj.tmp/
7 |
8 | # Package Files #
9 | *.jar
10 | *.war
11 | *.ear
12 |
13 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
14 | hs_err_pid*
15 |
16 | # Eclipse
17 | .classpath
18 | .project
19 | target
20 | .settings
21 |
22 | # Idea
23 | .idea
24 | *.iml
25 |
26 | # git
27 | *.orig
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 展示Apollo配置中心的各种使用场景和示例代码,目前包含了以下示例项目:
4 |
5 | * [spring-boot-logger](spring-boot-logger):演示[Spring Boot Logging](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html)如何通过Apollo配置中心实现动态调整Logging Level
6 | * [spring-cloud-logger](spring-cloud-logger):演示[Spring Boot Logging](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html)在Spring Cloud环境下如何通过Apollo配置中心方便地实现动态调整Logging Level
7 | * [spring-cloud-zuul](spring-cloud-zuul):演示[Spring Cloud Zuul](https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#netflix-zuul-reverse-proxy)如何通过Apollo配置中心实现动态路由
8 | * [spring-cloud-zuul-ratelimit](spring-cloud-zuul-ratelimit):演示[Spring Cloud Zuul](https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#netflix-zuul-reverse-proxy)的第三方限流插件[marcosbarbero/spring-cloud-zuul-ratelimit](https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit)如何通过Apollo配置中心实现动态限流
9 | * [spring-cloud-gateway](spring-cloud-gateway):演示[Spring Cloud Gateway](https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.3.RELEASE/single/spring-cloud-gateway.html)如何通过Apollo配置中心实现动态路由
10 | * [spring-boot-encrypt](spring-boot-encrypt):演示如何结合[jasypt-spring-boot](https://github.com/ulisesbocchio/jasypt-spring-boot)实现Apollo中存储加密配置
11 | * [dynamic-datasource](dynamic-datasource):演示[Spring Boot默认的HikariCP DataSource](https://github.com/brettwooldridge/HikariCP)如何通过Apollo配置中心实现动态切换数据源(其它类型的DataSource也是类似的,可以依样画葫芦)
12 | * [dubbo](dubbo): 演示[Dubbo](https://github.com/apache/incubator-dubbo)如何通过Apollo配置中心实现中心化配置
13 | * [spring-boot-dubbo](spring-boot-dubbo): 演示[Dubbo Spring Boot Starter](https://github.com/apache/incubator-dubbo-spring-boot-project)如何通过Apollo配置中心实现中心化配置
14 | * 该项目同时也演示了如何通过apollo管理logback的配置,详见[logback-spring.xml](https://github.com/ctripcorp/apollo-use-cases/blob/master/spring-boot-dubbo/spring-boot-dubbo-provider/src/main/resources/logback-spring.xml)
15 | * [netflix-archaius](netflix-archaius): 演示[Netflix Archaius](https://github.com/Netflix/archaius)如何使用Apollo配置中心作为其服务端使用
16 | * [sentinel](sentinel): 演示[Sentinel](https://github.com/alibaba/Sentinel)如何通过Apollo配置中心实现中心化流控规则配置
17 | * [properties-keeper](properties-keeper): 演示如何通过apollo管理启动前需要加载的properties文件配置
18 | * [spring-boot-agent](spring-boot-agent): 演示如何通过java agent探针技术实现应用无缝接入Apollo配置中心
19 | * [spring-mvc-logger](spring-mvc-logger): 演示Spring/SpringMVC项目下如何通过Apollo配置中心实现动态调整日志的属性值
20 |
21 | 欢迎大家把日常工作中的更多配置使用案例分享出来,提交Pull Request即可!
22 |
23 | # Instructions
24 |
25 | 1. 部署并启动Apollo配置中心
26 | * 请参考[分布式部署指南](https://github.com/ctripcorp/apollo/wiki/%E5%88%86%E5%B8%83%E5%BC%8F%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97)
27 | * 如果只是Demo用途的话,可以参考[Quick Start](https://github.com/ctripcorp/apollo/wiki/Quick-Start)文档快速地在本地启动一套Apollo配置中心,或者参考[Apollo开发指南](https://github.com/ctripcorp/apollo/wiki/Apollo%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97)通过IDE在本地启动一套Apollo配置中心
28 | 2. 配置Apollo Meta信息
29 | * Apollo支持应用在不同的环境有不同的配置,所以需要配置Apollo Meta信息
30 | * 示例代码在`application.properties`或System Property配置了`apollo.meta=http://localhost:8080`,请根据实际部署情况调整该配置
31 | 3. 以上步骤都完成后,就可以参考各子模块的README.md来运行示例项目了
32 |
--------------------------------------------------------------------------------
/dubbo/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Dubbo](https://github.com/apache/incubator-dubbo)如何通过Apollo配置中心实现中心化配置
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`dubbo`的项目
8 | 2. 在默认的`application`下配置zookeerper的地址,注意key为`zookeeper.address`
9 | * 如:zookeeper.address = 127.0.0.1:2181
10 | 3. 启动zookeeper
11 | 4. 运行`com.ctrip.framework.apollo.use.cases.dubbo.service.Server`启动Demo服务端
12 | 5. 运行`com.ctrip.framework.apollo.use.cases.dubbo.client.Consumer`启动Demo调用端
13 | 6. 在调用端输入任意字符后按回车,即可发起一次Dubbo服务请求并输出服务端的响应
14 | * 如输入`dubbo`,服务端会响应`Hello dubbo`
15 |
--------------------------------------------------------------------------------
/dubbo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | dubbo
13 |
14 |
15 |
16 | com.alibaba
17 | dubbo
18 |
19 |
20 | org.apache.zookeeper
21 | zookeeper
22 |
23 |
24 | log4j
25 | log4j
26 |
27 |
28 | slf4j-log4j12
29 | org.slf4j
30 |
31 |
32 |
33 |
34 | com.101tec
35 | zkclient
36 |
37 |
38 | org.apache.curator
39 | curator-framework
40 |
41 |
42 | org.apache.logging.log4j
43 | log4j-slf4j-impl
44 |
45 |
46 | org.apache.logging.log4j
47 | log4j-api
48 |
49 |
50 | org.apache.logging.log4j
51 | log4j-core
52 |
53 |
54 | org.slf4j
55 | log4j-over-slf4j
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/dubbo/src/main/java/com/ctrip/framework/apollo/use/cases/dubbo/api/DemoService.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dubbo.api;
2 |
3 | public interface DemoService {
4 |
5 | String sayHello(String name);
6 | }
7 |
--------------------------------------------------------------------------------
/dubbo/src/main/java/com/ctrip/framework/apollo/use/cases/dubbo/client/Consumer.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dubbo.client;
2 |
3 | import com.ctrip.framework.apollo.core.ConfigConsts;
4 | import com.ctrip.framework.apollo.use.cases.dubbo.api.DemoService;
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStreamReader;
8 | import java.nio.charset.StandardCharsets;
9 | import org.springframework.context.support.ClassPathXmlApplicationContext;
10 |
11 | public class Consumer {
12 |
13 | public static void main(String[] args) throws IOException {
14 | // set apollo meta server address, adjust to actual address if necessary
15 | System.setProperty(ConfigConsts.APOLLO_META_KEY, "http://localhost:8080");
16 |
17 | // run with -Djava.net.preferIPv4Stack=true if met 'Can't assign requested address' error
18 | ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
19 | context.start();
20 |
21 | DemoService demoService = (DemoService) context.getBean("demoService");
22 |
23 | System.out.println("Please input any key to test. Input quit to exit.");
24 | while (true) {
25 | System.out.print("> ");
26 | String input = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)).readLine();
27 | if (input == null || input.length() == 0) {
28 | continue;
29 | }
30 | input = input.trim();
31 | if (input.equalsIgnoreCase("quit")) {
32 | System.exit(0);
33 | }
34 | System.out.println(demoService.sayHello(input));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/dubbo/src/main/java/com/ctrip/framework/apollo/use/cases/dubbo/service/DemoServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dubbo.service;
2 |
3 | import com.ctrip.framework.apollo.use.cases.dubbo.api.DemoService;
4 |
5 | public class DemoServiceImpl implements DemoService {
6 |
7 | @Override
8 | public String sayHello(String name) {
9 | return "Hello " + name;
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/dubbo/src/main/java/com/ctrip/framework/apollo/use/cases/dubbo/service/Server.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dubbo.service;
2 |
3 | import com.ctrip.framework.apollo.core.ConfigConsts;
4 | import java.io.IOException;
5 | import org.springframework.context.support.ClassPathXmlApplicationContext;
6 |
7 | public class Server {
8 |
9 | public static void main(String[] args) throws IOException {
10 | // set apollo meta server address, adjust to actual address if necessary
11 | System.setProperty(ConfigConsts.APOLLO_META_KEY, "http://localhost:8080");
12 |
13 | // run with -Djava.net.preferIPv4Stack=true if met 'Can't assign requested address' error
14 | new ClassPathXmlApplicationContext("server.xml").start();
15 |
16 | System.in.read();
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/dubbo/src/main/resources/META-INF/app.properties:
--------------------------------------------------------------------------------
1 | app.id=dubbo
2 |
--------------------------------------------------------------------------------
/dubbo/src/main/resources/consumer.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
--------------------------------------------------------------------------------
/dubbo/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/dubbo/src/main/resources/server.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
--------------------------------------------------------------------------------
/dynamic-datasource/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Boot默认的HikariCP DataSource](https://github.com/brettwooldridge/HikariCP)如何通过Apollo配置中心实现动态切换数据源(其它类型的DataSource也是类似的,可以依样画葫芦)
4 |
5 | # Instructions
6 |
7 | 1. 创建test1数据库,导入test1.sql
8 | 2. 创建test2数据库,导入test2.sql
9 | 3. 在Apollo配置中心创建AppId为`dynamic-datasource`的项目
10 | 2. 在默认的`application`下做如下配置(按照实际的数据库连接信息填写):
11 |
12 | ```properties
13 | spring.datasource.url = jdbc:mysql://127.0.0.1:3306/test1?autoReconnect=true&useUnicode=true&characterEncoding=utf-8
14 | spring.datasource.username = xxx-user
15 | spring.datasource.password = xxx-password
16 | # hikari specific settings
17 | spring.datasource.hikari.maximumPoolSize = 1
18 | ```
19 | 3. 运行`com.ctrip.framework.apollo.use.cases.dynamic.datasource.Application`启动Demo
20 | 4. 程序启动后会持续打印kl
21 | 5. 在Apollo配置中心修改配置,把`spring.datasource.url`的值切换到`test2`并发布配置
22 | 6. 程序会持续打印ckl,说明动态切换数据源生效了
23 | 7. 更多信息可以参见博文:http://www.kailing.pub/article/index/arcid/198.html
24 |
--------------------------------------------------------------------------------
/dynamic-datasource/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | dynamic-datasource
13 |
14 |
15 | org.springframework.boot
16 | spring-boot-starter
17 |
18 |
19 | org.springframework.cloud
20 | spring-cloud-context
21 |
22 |
23 | mysql
24 | mysql-connector-java
25 |
26 |
27 | com.zaxxer
28 | HikariCP
29 | 2.7.8
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-data-jpa
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import com.ctrip.framework.apollo.use.cases.dynamic.datasource.repository.UserRepository;
5 | import java.util.concurrent.Executors;
6 | import java.util.concurrent.TimeUnit;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.CommandLineRunner;
9 | import org.springframework.boot.SpringApplication;
10 | import org.springframework.boot.autoconfigure.SpringBootApplication;
11 | import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
12 |
13 | /**
14 | * Created by kl on 2018/6/25. Content :动态数据源实例
15 | */
16 | @EnableApolloConfig
17 | @SpringBootApplication
18 | public class Application implements CommandLineRunner {
19 |
20 | public static void main(String[] args) {
21 | SpringApplication.run(Application.class, args);
22 | }
23 |
24 | @Autowired
25 | private UserRepository userRepository;
26 |
27 | @Override
28 | public void run(String... args) throws Exception {
29 | Executors.newSingleThreadExecutor().submit(() -> {
30 | while (true) {
31 | try {
32 | System.err.println(userRepository.findById(1).get().getName());
33 | TimeUnit.SECONDS.sleep(1);
34 | } catch (Throwable ex) {
35 | ex.printStackTrace();
36 | }
37 | }
38 | });
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/RefreshableDataSourceConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource;
2 |
3 | import com.ctrip.framework.apollo.use.cases.dynamic.datasource.ds.DynamicDataSource;
4 | import com.ctrip.framework.apollo.use.cases.dynamic.datasource.util.DataSourceManager;
5 | import javax.sql.DataSource;
6 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
7 | import org.springframework.boot.context.properties.ConfigurationProperties;
8 | import org.springframework.cloud.context.config.annotation.RefreshScope;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Primary;
12 |
13 | @Configuration
14 | public class RefreshableDataSourceConfiguration {
15 |
16 | @Bean
17 | public DynamicDataSource dataSource(DataSourceManager dataSourceManager) {
18 | DataSource actualDataSource = dataSourceManager.createDataSource();
19 | return new DynamicDataSource(actualDataSource);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/ds/DynamicDataSource.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.ds;
2 |
3 | import java.io.PrintWriter;
4 | import java.sql.Connection;
5 | import java.sql.SQLException;
6 | import java.sql.SQLFeatureNotSupportedException;
7 | import java.util.concurrent.atomic.AtomicReference;
8 | import javax.sql.DataSource;
9 |
10 | /**
11 | * A sample refreshable data source
12 | */
13 | public class DynamicDataSource implements DataSource {
14 | private final AtomicReference dataSourceAtomicReference;
15 |
16 | public DynamicDataSource(DataSource dataSource) {
17 | dataSourceAtomicReference = new AtomicReference<>(dataSource);
18 | }
19 |
20 | /**
21 | * set the new data source and return the previous one
22 | */
23 | public DataSource setDataSource(DataSource newDataSource){
24 | return dataSourceAtomicReference.getAndSet(newDataSource);
25 | }
26 |
27 | @Override
28 | public Connection getConnection() throws SQLException {
29 | return dataSourceAtomicReference.get().getConnection();
30 | }
31 |
32 | @Override
33 | public Connection getConnection(String username, String password) throws SQLException {
34 | return dataSourceAtomicReference.get().getConnection(username, password);
35 | }
36 |
37 | @Override
38 | public T unwrap(Class iface) throws SQLException {
39 | return dataSourceAtomicReference.get().unwrap(iface);
40 | }
41 |
42 | @Override
43 | public boolean isWrapperFor(Class> iface) throws SQLException {
44 | return dataSourceAtomicReference.get().isWrapperFor(iface);
45 | }
46 |
47 | @Override
48 | public PrintWriter getLogWriter() throws SQLException {
49 | return dataSourceAtomicReference.get().getLogWriter();
50 | }
51 |
52 | @Override
53 | public void setLogWriter(PrintWriter out) throws SQLException {
54 | dataSourceAtomicReference.get().setLogWriter(out);
55 | }
56 |
57 | @Override
58 | public void setLoginTimeout(int seconds) throws SQLException {
59 | dataSourceAtomicReference.get().setLoginTimeout(seconds);
60 | }
61 |
62 | @Override
63 | public int getLoginTimeout() throws SQLException {
64 | return dataSourceAtomicReference.get().getLoginTimeout();
65 | }
66 |
67 | @Override
68 | public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {
69 | return dataSourceAtomicReference.get().getParentLogger();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/entity/User.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.entity;
2 |
3 | import javax.persistence.Entity;
4 | import javax.persistence.Id;
5 |
6 | /**
7 | * Created by kl on 2018/6/25. Content :
8 | */
9 | @Entity
10 | public class User {
11 |
12 | @Id
13 | private int id;
14 | private String name;
15 |
16 | public int getId() {
17 | return id;
18 | }
19 |
20 | public void setId(int id) {
21 | this.id = id;
22 | }
23 |
24 | public String getName() {
25 | return name;
26 | }
27 |
28 | public void setName(String name) {
29 | this.name = name;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/repository/UserRepository.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.repository;
2 |
3 | import com.ctrip.framework.apollo.use.cases.dynamic.datasource.entity.User;
4 | import org.springframework.data.repository.PagingAndSortingRepository;
5 |
6 | public interface UserRepository extends PagingAndSortingRepository {
7 | }
8 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/util/CustomizedConfigurationPropertiesBinder.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.util;
2 |
3 | import java.util.function.Consumer;
4 | import org.springframework.beans.BeansException;
5 | import org.springframework.beans.PropertyEditorRegistry;
6 | import org.springframework.boot.context.properties.bind.BindHandler;
7 | import org.springframework.boot.context.properties.bind.Bindable;
8 | import org.springframework.boot.context.properties.bind.Binder;
9 | import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
10 | import org.springframework.boot.context.properties.bind.handler.IgnoreTopLevelConverterNotFoundBindHandler;
11 | import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
12 | import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
13 | import org.springframework.context.ApplicationContext;
14 | import org.springframework.context.ApplicationContextAware;
15 | import org.springframework.context.ConfigurableApplicationContext;
16 | import org.springframework.core.convert.ConversionService;
17 | import org.springframework.core.env.PropertySources;
18 | import org.springframework.stereotype.Component;
19 |
20 | /**
21 | * @see org.springframework.boot.context.properties.ConfigurationPropertiesBinder
22 | */
23 | @Component
24 | public class CustomizedConfigurationPropertiesBinder implements ApplicationContextAware {
25 |
26 | private ConfigurableApplicationContext applicationContext;
27 | private PropertySources propertySources;
28 | private Binder binder;
29 |
30 | public void bind(String configPrefix, Bindable> bean) {
31 | BindHandler handler = new IgnoreTopLevelConverterNotFoundBindHandler();
32 | this.binder.bind(configPrefix, bean, handler);
33 | }
34 |
35 | @Override
36 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
37 | this.applicationContext = (ConfigurableApplicationContext) applicationContext;
38 | this.propertySources = this.applicationContext.getEnvironment().getPropertySources();
39 | this.binder = new Binder(getConfigurationPropertySources(), getPropertySourcesPlaceholdersResolver(),
40 | getConversionService(), getPropertyEditorInitializer());
41 | }
42 |
43 | private Iterable getConfigurationPropertySources() {
44 | return ConfigurationPropertySources.from(this.propertySources);
45 | }
46 |
47 | private PropertySourcesPlaceholdersResolver getPropertySourcesPlaceholdersResolver() {
48 | return new PropertySourcesPlaceholdersResolver(this.propertySources);
49 | }
50 |
51 | private ConversionService getConversionService() {
52 | return this.applicationContext.getBeanFactory().getConversionService();
53 | }
54 |
55 | private Consumer getPropertyEditorInitializer() {
56 | return this.applicationContext.getBeanFactory()::copyRegisteredEditorsTo;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/util/DataSourceManager.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.util;
2 |
3 | import com.zaxxer.hikari.HikariDataSource;
4 | import java.sql.Connection;
5 | import java.sql.SQLException;
6 | import javax.sql.DataSource;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.factory.annotation.Autowired;
10 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
11 | import org.springframework.boot.context.properties.bind.Bindable;
12 | import org.springframework.stereotype.Component;
13 | import org.springframework.util.StringUtils;
14 |
15 | @Component
16 | public class DataSourceManager {
17 |
18 | private static final Logger logger = LoggerFactory.getLogger(DataSourceManager.class);
19 |
20 | @Autowired
21 | private CustomizedConfigurationPropertiesBinder binder;
22 |
23 | @Autowired
24 | private DataSourceProperties dataSourceProperties;
25 |
26 | /**
27 | * create a hikari data source
28 | * @see org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari#dataSource
29 | */
30 | public HikariDataSource createDataSource() {
31 | HikariDataSource dataSource = dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class).build();
32 | if (StringUtils.hasText(dataSourceProperties.getName())) {
33 | dataSource.setPoolName(dataSourceProperties.getName());
34 | }
35 | Bindable> target = Bindable.of(HikariDataSource.class).withExistingValue(dataSource);
36 | this.binder.bind("spring.datasource.hikari", target);
37 | return dataSource;
38 | }
39 |
40 | public HikariDataSource createAndTestDataSource() throws SQLException {
41 | HikariDataSource newDataSource = createDataSource();
42 | try {
43 | testConnection(newDataSource);
44 | } catch (SQLException ex) {
45 | logger.error("Testing connection for data source failed: {}", newDataSource.getJdbcUrl(), ex);
46 | newDataSource.close();
47 | throw ex;
48 | }
49 |
50 | return newDataSource;
51 | }
52 |
53 | private void testConnection(DataSource dataSource) throws SQLException {
54 | //borrow a connection
55 | Connection connection = dataSource.getConnection();
56 | //return the connection
57 | connection.close();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/util/DataSourceRefresher.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.util;
2 |
3 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
4 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
5 | import com.ctrip.framework.apollo.use.cases.dynamic.datasource.ds.DynamicDataSource;
6 | import java.util.Set;
7 | import java.util.concurrent.Executors;
8 | import java.util.concurrent.ScheduledExecutorService;
9 | import java.util.concurrent.TimeUnit;
10 | import javax.sql.DataSource;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 | import org.springframework.beans.BeansException;
14 | import org.springframework.beans.factory.annotation.Autowired;
15 | import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
16 | import org.springframework.context.ApplicationContext;
17 | import org.springframework.context.ApplicationContextAware;
18 | import org.springframework.stereotype.Component;
19 |
20 | @Component
21 | public class DataSourceRefresher implements ApplicationContextAware {
22 |
23 | private static final Logger logger = LoggerFactory.getLogger(DataSourceRefresher.class);
24 |
25 | private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
26 |
27 | @Autowired
28 | private DynamicDataSource dynamicDataSource;
29 |
30 | @Autowired
31 | private DataSourceManager dataSourceManager;
32 |
33 | @Autowired
34 | private ApplicationContext applicationContext;
35 |
36 | @ApolloConfigChangeListener(interestedKeyPrefixes = "spring.datasource.")
37 | public void onChange(ConfigChangeEvent changeEvent) {
38 | refreshDataSource(changeEvent.changedKeys());
39 | }
40 |
41 | private synchronized void refreshDataSource(Set changedKeys) {
42 | try {
43 | logger.info("Refreshing data source");
44 |
45 | /**
46 | * rebind configuration beans, e.g. DataSourceProperties
47 | * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
48 | */
49 | this.applicationContext.publishEvent(new EnvironmentChangeEvent(changedKeys));
50 |
51 | DataSource newDataSource = dataSourceManager.createAndTestDataSource();
52 | DataSource oldDataSource = dynamicDataSource.setDataSource(newDataSource);
53 | asyncTerminate(oldDataSource);
54 |
55 | logger.info("Finished refreshing data source");
56 | } catch (Throwable ex) {
57 | logger.error("Refreshing data source failed", ex);
58 | }
59 | }
60 |
61 | private void asyncTerminate(DataSource dataSource) {
62 | DataSourceTerminationTask task = new DataSourceTerminationTask(dataSource, scheduledExecutorService);
63 |
64 | //start now
65 | scheduledExecutorService.schedule(task, 0, TimeUnit.MILLISECONDS);
66 | }
67 |
68 | @Override
69 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
70 | this.applicationContext = applicationContext;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/java/com/ctrip/framework/apollo/use/cases/dynamic/datasource/util/DataSourceTerminationTask.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.dynamic.datasource.util;
2 |
3 | import com.zaxxer.hikari.HikariDataSource;
4 | import com.zaxxer.hikari.HikariPoolMXBean;
5 | import java.util.concurrent.ScheduledExecutorService;
6 | import java.util.concurrent.TimeUnit;
7 | import javax.sql.DataSource;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | public class DataSourceTerminationTask implements Runnable {
12 |
13 | private static final Logger logger = LoggerFactory.getLogger(DataSourceTerminationTask.class);
14 | private static final int MAX_RETRY_TIMES = 10;
15 | private static final int RETRY_DELAY_IN_MILLISECONDS = 5000;
16 |
17 | private final DataSource dataSourceToTerminate;
18 | private final ScheduledExecutorService scheduledExecutorService;
19 |
20 | private volatile int retryTimes;
21 |
22 | public DataSourceTerminationTask(DataSource dataSourceToTerminate,
23 | ScheduledExecutorService scheduledExecutorService) {
24 | this.dataSourceToTerminate = dataSourceToTerminate;
25 | this.scheduledExecutorService = scheduledExecutorService;
26 | this.retryTimes = 0;
27 | }
28 |
29 | @Override
30 | public void run() {
31 | if (terminate(dataSourceToTerminate)) {
32 | logger.info("Data source {} terminated successfully!", dataSourceToTerminate);
33 | } else {
34 | scheduledExecutorService.schedule(this, RETRY_DELAY_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
35 | }
36 | }
37 |
38 | private boolean terminate(DataSource dataSource) {
39 | logger.info("Trying to terminate data source: {}", dataSource);
40 |
41 | try {
42 | if (dataSource instanceof HikariDataSource) {
43 | return terminateHikariDataSource((HikariDataSource) dataSource);
44 | }
45 |
46 | logger.error("Not supported data source: {}", dataSource);
47 |
48 | return true;
49 | } catch (Throwable ex) {
50 | logger.warn("Terminating data source {} failed, will retry in {} ms, error message: {}", dataSource,
51 | RETRY_DELAY_IN_MILLISECONDS, ex.getMessage());
52 | return false;
53 | } finally {
54 | retryTimes++;
55 | }
56 | }
57 |
58 | /**
59 | * @see Support graceful shutdown of connection
60 | * pool
61 | */
62 | private boolean terminateHikariDataSource(HikariDataSource dataSource) {
63 | HikariPoolMXBean poolMXBean = dataSource.getHikariPoolMXBean();
64 |
65 | if (poolMXBean != null) {
66 | //evict idle connections
67 | poolMXBean.softEvictConnections();
68 |
69 | if (poolMXBean.getActiveConnections() > 0 && retryTimes < MAX_RETRY_TIMES) {
70 | logger.warn("Data source {} still has {} active connections, will retry in {} ms.",
71 | dataSource,
72 | poolMXBean.getActiveConnections(), RETRY_DELAY_IN_MILLISECONDS);
73 | return false;
74 | }
75 |
76 | if (poolMXBean.getActiveConnections() > 0) {
77 | logger.warn(
78 | "Retry times({}) >= {}, force closing data source {}, with {} active connections!",
79 | retryTimes,
80 | MAX_RETRY_TIMES, dataSource, poolMXBean.getActiveConnections());
81 | }
82 | }
83 |
84 | dataSource.close();
85 |
86 | return true;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=dynamic-datasource
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/resources/sql/test1.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat MySQL Data Transfer
3 |
4 | Source Server : 本地数据库
5 | Source Server Version : 50624
6 | Source Host : 127.0.0.1:3306
7 | Source Database : test1
8 |
9 | Target Server Type : MYSQL
10 | Target Server Version : 50624
11 | File Encoding : 65001
12 |
13 | Date: 2018-06-25 17:56:09
14 | */
15 |
16 | SET FOREIGN_KEY_CHECKS=0;
17 |
18 | -- ----------------------------
19 | -- Table structure for user
20 | -- ----------------------------
21 | DROP TABLE IF EXISTS `user`;
22 | CREATE TABLE `user` (
23 | `id` int(11) NOT NULL,
24 | `name` varchar(255) DEFAULT NULL,
25 | PRIMARY KEY (`id`)
26 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
27 |
28 | -- ----------------------------
29 | -- Records of user
30 | -- ----------------------------
31 | INSERT INTO `user` VALUES ('1', 'kl');
32 |
--------------------------------------------------------------------------------
/dynamic-datasource/src/main/resources/sql/test2.sql:
--------------------------------------------------------------------------------
1 | /*
2 | Navicat MySQL Data Transfer
3 |
4 | Source Server : 本地数据库
5 | Source Server Version : 50624
6 | Source Host : 127.0.0.1:3306
7 | Source Database : test2
8 |
9 | Target Server Type : MYSQL
10 | Target Server Version : 50624
11 | File Encoding : 65001
12 |
13 | Date: 2018-06-25 17:56:16
14 |
15 |
16 |
17 |
18 |
19 |
20 | */
21 |
22 | SET FOREIGN_KEY_CHECKS=0;
23 |
24 | -- ----------------------------
25 | -- Table structure for user
26 | -- ----------------------------
27 | DROP TABLE IF EXISTS `user`;
28 | CREATE TABLE `user` (
29 | `id` int(11) NOT NULL,
30 | `name` varchar(255) DEFAULT NULL,
31 | PRIMARY KEY (`id`)
32 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
33 |
34 | -- ----------------------------
35 | -- Records of user
36 | -- ----------------------------
37 | INSERT INTO `user` VALUES ('1', 'ckl');
38 |
--------------------------------------------------------------------------------
/netflix-archaius/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | Netflix开源的一系列组件(Zuul, Hystrix, Eureka, Ribbon)的配置管理使用的是自家的[Archaius](https://github.com/Netflix/archaius),遗憾的是Netflix没有开源Archaius的服务端,现在可以使用Apollo作为Archaius的服务端使用。
4 |
5 | - Apollo可以为Netflix微服务全家桶提供集中化的配置管理
6 | - Archaius可以做为Apollo Client的另一种选择
7 |
8 | # Instructions
9 |
10 | 1. 在Apollo配置中心创建AppId为`netflix-archaius`的项目
11 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
12 |
13 | ```properties
14 | hystrix.command.default.circuitBreaker.forceClosed = false
15 | ```
16 | 3. 运行`com.ctrip.framework.apollo.use.cases.netflix.archaius.Application`启动Demo
17 | 4. 程序会在控制台输出hystrix.command.default.circuitBreaker.forceClosed参数值的变化
18 | 5. 在Apollo配置中心修改配置,把`hystrix.command.default.circuitBreaker.forceClosed`的值改为`true`并发布配置
19 | * Archaius 默认30秒从服务端更新一次配置信息,所以需要等待30秒配置生效
20 |
--------------------------------------------------------------------------------
/netflix-archaius/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | netflix-archaius
13 |
14 |
15 |
16 | com.netflix.archaius
17 | archaius-core
18 |
19 |
20 | org.slf4j
21 | jcl-over-slf4j
22 |
23 |
24 | org.slf4j
25 | log4j-over-slf4j
26 |
27 |
28 | org.apache.logging.log4j
29 | log4j-slf4j-impl
30 |
31 |
32 | org.apache.logging.log4j
33 | log4j-api
34 |
35 |
36 | org.apache.logging.log4j
37 | log4j-core
38 |
39 |
40 |
--------------------------------------------------------------------------------
/netflix-archaius/src/main/java/com/ctrip/framework/apollo/use/cases/netflix/archaius/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.netflix.archaius;
2 |
3 | import com.netflix.config.DynamicBooleanProperty;
4 | import com.netflix.config.DynamicPropertyFactory;
5 | import java.time.LocalDateTime;
6 |
7 | public class Application {
8 |
9 | private static final String ARCHAIUS_ADD_ITIONAL_URLS = "archaius.configurationSource.additionalUrls";
10 |
11 | public static void main(String[] args) throws Exception {
12 | // set apollo meta server address, adjust to actual address if necessary
13 | String apolloConfigServiceUrl = "http://localhost:8080";
14 |
15 | String appId = "netflix-archaius";
16 | System.setProperty(ARCHAIUS_ADD_ITIONAL_URLS,
17 | apolloConfigServiceUrl + "/configfiles/" + appId + "/default/application");
18 |
19 | DynamicBooleanProperty hystrixForceClosedProperty = DynamicPropertyFactory.getInstance()
20 | .getBooleanProperty("hystrix.command.default.circuitBreaker.forceClosed", false);
21 |
22 | //Archaius 默认30秒从服务端更新一次配置信息
23 | hystrixForceClosedProperty.addCallback(
24 | () -> System.out.println("[" + LocalDateTime.now().toString() + "] update forceClosed :" + hystrixForceClosedProperty.get()));
25 |
26 | while (true) {
27 | System.in.read();
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/netflix-archaius/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.ctrip.framework.apollo
8 | apollo-use-cases
9 | pom
10 | 1.0-SNAPSHOT
11 |
12 | dubbo
13 | spring-boot-dubbo
14 | spring-cloud-zuul
15 | spring-cloud-zuul-ratelimit
16 | spring-boot-logger
17 | dynamic-datasource
18 | spring-boot-encrypt
19 | spring-cloud-logger
20 | netflix-archaius
21 | sentinel
22 | properties-keeper
23 | spring-boot-agent
24 | spring-cloud-gateway
25 | spring-mvc-logger
26 |
27 |
28 |
29 | 1.8
30 | UTF-8
31 | 1.9.2
32 | Cairo-SR2
33 | Finchley.RELEASE
34 | 2.6.12
35 | 0.2.0
36 | 3.4.14
37 | 0.10
38 | 2.12.0
39 | 0.7.6
40 | 2.2.4.RELEASE
41 | 2.17.1
42 |
43 |
44 |
45 |
46 |
47 |
48 | io.spring.platform
49 | platform-bom
50 | ${platform.bom.version}
51 | pom
52 | import
53 |
54 |
55 |
56 | org.springframework.cloud
57 | spring-cloud-dependencies
58 | ${spring.cloud.version}
59 | pom
60 | import
61 |
62 |
63 |
64 | com.ctrip.framework.apollo
65 | apollo-client
66 | ${apollo.version}
67 |
68 |
69 |
70 | com.alibaba
71 | dubbo
72 | ${dubbo.version}
73 |
74 |
75 | com.alibaba.boot
76 | dubbo-spring-boot-starter
77 | ${dubbo.spring.boot.starter.version}
78 |
79 |
80 |
81 | org.apache.zookeeper
82 | zookeeper
83 | ${zookeeper.version}
84 |
85 |
86 | com.101tec
87 | zkclient
88 | ${zkclient.version}
89 |
90 |
91 | org.apache.curator
92 | curator-framework
93 | ${curator.version}
94 |
95 |
96 | org.apache.curator
97 | curator-recipes
98 | ${curator.version}
99 |
100 |
101 | org.apache.curator
102 | curator-x-discovery
103 | ${curator.version}
104 |
105 |
106 | org.apache.curator
107 | curator-test
108 | ${curator.version}
109 |
110 |
111 |
112 | org.apache.logging.log4j
113 | log4j-api
114 | ${log4j2.version}
115 |
116 |
117 | org.apache.logging.log4j
118 | log4j-slf4j-impl
119 | ${log4j2.version}
120 |
121 |
122 | org.apache.logging.log4j
123 | log4j-core
124 | ${log4j2.version}
125 |
126 |
127 | org.apache.logging.log4j
128 | log4j-jul
129 | ${log4j2.version}
130 |
131 |
132 |
133 | com.netflix.archaius
134 | archaius-core
135 | ${archaius.version}
136 |
137 |
138 | commons-logging
139 | commons-logging
140 |
141 |
142 | com.google.code.findbugs
143 | annotations
144 |
145 |
146 |
147 |
148 | com.marcosbarbero.cloud
149 | spring-cloud-zuul-ratelimit
150 | ${spring.cloud.zuul.ratelimit.version}
151 |
152 |
153 |
154 |
155 |
156 |
157 | com.ctrip.framework.apollo
158 | apollo-client
159 |
160 |
161 |
162 |
163 |
164 |
165 | org.apache.maven.plugins
166 | maven-compiler-plugin
167 | 3.3
168 |
169 | true
170 | ${java.version}
171 | ${java.version}
172 | ${project.encoding}
173 |
174 |
175 |
176 | org.apache.maven.plugins
177 | maven-jar-plugin
178 | 2.6
179 |
180 | ${project.artifactId}-${project.version}
181 |
182 |
183 |
184 | org.apache.maven.plugins
185 | maven-source-plugin
186 | 2.4
187 |
188 | ${project.artifactId}-${project.version}
189 |
190 |
191 |
192 |
193 |
194 |
--------------------------------------------------------------------------------
/properties-keeper/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示如何通过apollo管理将启动前需要加载的properties文件配置也管理起来, 常见于数据源或者一些不怎么变更的文件properties配置, 主要抽离文件配置的重复内容和屏蔽环境差异, 一般用于容器环境, 通过ENV注入所需变量配置.
4 |
5 | # Instructions
6 | - 约定应用加载的配置文件名称, 放到公共namespace抽离公共配置, 项目有需要特别修改的, 直接关联公共namespace覆盖或增加响应配置. 示例中使用`config.properties`和`db-config.properties`, 对应公共namespace `PLATFORM.config.properties`和`PLATFORM.db-config.properties`
7 | - config.properties: 不经常修改或者非数据源但启动前需要加载的配置项
8 | - db-config.properties: 数据源相关的配置
9 | - 确保需要托管文件在项目中存在, 空文件也可.
10 | - 选择apollo托管则全部以apollo上配置为准, 文件内已有内容会被覆盖.
11 | - 除了公共properties文件外, 其他properties文件默认也会被扫到并从apollo拉取, 如果不存在会报错退出, 建立对应文件名的私有namespace即可, 如果不想404直接退出,可以传入环境变量 `APOLLO_FORCE_PROPERTIES=false`.
12 |
--------------------------------------------------------------------------------
/properties-keeper/fetch_properties.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | CONFIG_SERVER_BASE_URL=${CONFIG_SERVER_BASE_URL:?"need env param [CONFIG_SERVER_BASE_URL]"}
3 | APOLLO_APP_ID=${APOLLO_APP_ID:?"need env param [APOLLO_APP_ID]"}
4 | PUBLIC_PREFIX=${PUBLIC_PREFIX:-"PLATFORM"}
5 | APOLLO_CLUSTER=${APOLLO_CLUSTER:-"default"}
6 | CONFIG_FILES="$(find /data/tomcat/webapps/ROOT/WEB-INF/classes/ -name "*.properties" ! -path "*static*" | awk -vFS="/" '{print $NF}')"
7 | ECS_IP=$(curl http://100.100.100.200/latest/meta-data/private-ipv4)
8 | APOLLO_FORCE_PROPERTIES=${APOLLO_FORCE_PROPERTIES:-"true"}
9 |
10 |
11 | for _config_file in ${CONFIG_FILES[@]}
12 | do
13 | export result=
14 | echo "## $_config_file"
15 | _abs_config_file_path=`find /data/tomcat/webapps/ROOT/WEB-INF/classes/ -name "$_config_file" ! -path "*static*"`
16 | _namespace=${_config_file%%.*}
17 |
18 | if [ x"$_namespace" = x"config" ] || [ x"$_namespace" = x"db-config" ];then
19 | _namespace="${PUBLIC_PREFIX}.${_namespace}"
20 | fi
21 |
22 | result=$(curl -s ${CONFIG_SERVER_BASE_URL}/configfiles/${APOLLO_APP_ID}/${APOLLO_CLUSTER}/${_namespace}?ip=${ECS_IP} | tr -d '\\' )
23 | if echo $result | grep -q 'Not Found' ;then
24 | echo "################# $_config_file not found in apollo!!! #######################"
25 | if [ x"$APOLLO_FORCE_PROPERTIES" = x"true" ];then
26 | exit 404
27 | fi
28 | continue
29 | fi
30 | echo "### init ${_abs_config_file_path} ..."
31 | echo "$result" | sort > ${_abs_config_file_path}
32 | done
--------------------------------------------------------------------------------
/properties-keeper/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | properties-keeper
13 |
14 |
15 |
--------------------------------------------------------------------------------
/sentinel/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Sentinel](https://github.com/alibaba/Sentinel)如何通过Apollo配置中心实现中心化流控规则配置
4 |
5 | # Instructions
6 |
7 | - [基于Apollo配置中心的Sentinel流控规则配置实战(一)](https://mp.weixin.qq.com/s?__biz=MzA4NzA0NjAzOQ==&mid=2257484007&idx=1&sn=26e228c98d0743df098969be4a86f106&chksm=9345ba1fa43233090885ba37b601ed9f28e278c6585b5c73c5e6d801615240defd48786a79f5&token=159781885&lang=zh_CN&scene=21#wechat_redirect)
8 | - [基于Apollo配置中心的Sentinel流控规则配置实战(二) ](https://mp.weixin.qq.com/s?__biz=MzA4NzA0NjAzOQ==&mid=2257484017&idx=1&sn=cce834ad61e172a439f86e188e243144&chksm=9345ba09a432331fe5652240c6bebe50d687e58e7a82e1fcd2911a4b43f14c86a5c426c1c731&scene=0&xtrack=1#rd)
9 |
--------------------------------------------------------------------------------
/sentinel/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | sentinel
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spring-boot-agent/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示如何通过java agent技术无缝集成Apollo配置中心,典型使用场如:
4 |
5 | 1. 有些配置已经打到jar包里了,而源码不方便修改
6 | 2. 不想改动项目代码直接集成Apollo
7 |
8 | PS: 通过java agent的偷懒方式有缺陷,很难使用到配置变更动态生效功能,这里只是提供场景实例思路,最好还是按照官方wiki的方式正确接入,也非常简单
9 | # Instructions
10 |
11 | 1. 在Apollo配置中心创建AppId为`spring-boot-agent`的项目
12 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
13 |
14 | ```properties
15 | test.input = 666
16 | ```
17 | 3. 运行`com.ctrip.framework.apollo.use.cases.agent.Application`启动Demo,程序会打印application.properties配置的`888`
18 | 4. 编译apollo-agent模块,得到apollo-agent-1.0-SNAPSHOT.jar,然后在VM options中,添加如下javaagent配置:
19 | ```
20 | -javaagent:xxx\apollo-agent-1.0-SNAPSHOT.jar
21 | -Ddev_meta=http://127.0.0.1:8801
22 | -Denv=DEV
23 | -Dapp.id=spring-boot-agent
24 | ```
25 | javaagent需要自行替换apollo-agent-1.0-SNAPSHOT.jar的决定路径
26 | 5. 重新运行`com.ctrip.framework.apollo.use.cases.agent.Application`启动Demo,这个时候就会输出apollo中配置的`666`了
27 |
--------------------------------------------------------------------------------
/spring-boot-agent/apollo-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-boot-agent
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | apollo-agent
13 |
14 |
15 | com.ctrip.framework.apollo.use.cases.agent.ApolloAgent
16 |
17 |
18 |
19 |
20 |
21 | org.apache.maven.plugins
22 | maven-jar-plugin
23 | 2.6
24 |
25 |
26 |
27 | ${premain.class}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/spring-boot-agent/apollo-agent/src/main/java/com/ctrip/framework/apollo/use/cases/agent/ApolloAgent.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.agent;
2 |
3 | import com.ctrip.framework.apollo.Config;
4 | import com.ctrip.framework.apollo.ConfigChangeListener;
5 | import com.ctrip.framework.apollo.ConfigService;
6 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
7 | import com.ctrip.framework.apollo.spring.property.AutoUpdateConfigChangeListener;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.lang.instrument.Instrumentation;
12 |
13 | /**
14 | * @author: kl @kailing.pub
15 | * @date: 2019/5/8
16 | */
17 | public class ApolloAgent {
18 |
19 | private static final Logger LOGGER = LoggerFactory.getLogger(ApolloAgent.class);
20 |
21 | public static void premain(String agentArgs, Instrumentation inst) {
22 | Config config = ConfigService.getAppConfig();
23 | for (String key : config.getPropertyNames()) {
24 | System.getProperties().put(key, config.getProperty(key, ""));
25 | }
26 | LOGGER.debug("Apollo Configure load complete");
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/spring-boot-agent/app/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-boot-agent
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | app
13 |
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spring-boot-agent/app/src/main/java/com.ctrip.framevowrk.apollo.use.cases.agent/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framevowrk.apollo.use.cases.agent;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.boot.CommandLineRunner;
5 | import org.springframework.boot.SpringApplication;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 |
8 | import java.util.concurrent.TimeUnit;
9 |
10 | /**
11 | * @author: kl @kailing.pub
12 | * @date: 2019/5/8
13 | */
14 | @SpringBootApplication
15 | public class Application implements CommandLineRunner{
16 |
17 |
18 | @Value("${test.input:777}")
19 | private String input;
20 |
21 | @Override
22 | public void run(String... args) {
23 | while (true){
24 | System.err.println(input);
25 | try {
26 | TimeUnit.SECONDS.sleep(2);
27 | }catch (InterruptedException e){
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | }
33 |
34 | public static void main(String[] args) {
35 | SpringApplication.run(Application.class, args);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/spring-boot-agent/app/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | test.input = 888
2 |
--------------------------------------------------------------------------------
/spring-boot-agent/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 | pom
12 | spring-boot-agent
13 |
14 |
15 | apollo-agent
16 | app
17 |
18 |
19 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Dubbo Spring Boot Starter](https://github.com/apache/incubator-dubbo-spring-boot-project)如何通过Apollo配置中心实现中心化配置
4 |
5 | # Instructions
6 |
7 | ## 服务端配置
8 | 1. 在Apollo配置中心创建AppId为`spring-boot-dubbo-provider`的项目
9 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
10 |
11 | ```properties
12 | # Base packages to scan Dubbo Components (e.g @Service , @Reference)
13 | dubbo.scan.basePackages = com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.provider
14 |
15 | ## ApplicationConfig Bean
16 | dubbo.application.name = spring-boot-dubbo-provider
17 |
18 | ## RegistryConfig Bean
19 | dubbo.registry.protocol = zookeeper
20 | dubbo.registry.address = 127.0.0.1:2181
21 | ```
22 | 3. 启动zookeeper
23 | 4. 运行`com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.provider.Server`启动Demo服务端
24 |
25 | ## 调用端配置
26 | 1. 在Apollo配置中心创建AppId为`spring-boot-dubbo-consumer`的项目
27 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
28 |
29 | ```properties
30 | ## ApplicationConfig Bean
31 | dubbo.application.name = spring-boot-dubbo-consumer
32 |
33 | ## RegistryConfig Bean
34 | dubbo.registry.protocol = zookeeper
35 | dubbo.registry.address = 127.0.0.1:2181
36 | ```
37 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.consumer.Consumer`启动Demo调用端
38 | 4. 在调用端输入任意字符后按回车,即可发起一次Dubbo服务请求并输出服务端的响应
39 | * 如输入`dubbo`,服务端会响应`Hello dubbo`
40 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-dubbo
13 | pom
14 |
15 | spring-boot-dubbo-api
16 | spring-boot-dubbo-consumer
17 | spring-boot-dubbo-provider
18 |
19 |
20 |
21 |
22 |
23 | com.ctrip.framework.apollo
24 | spring-boot-dubbo-api
25 | ${project.version}
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-boot-dubbo
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-dubbo-api
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-api/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/starter/dubbo/api/DemoService.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.api;
2 |
3 | public interface DemoService {
4 |
5 | String sayHello(String name);
6 | }
7 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-consumer/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-boot-dubbo
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-dubbo-consumer
13 |
14 |
15 |
16 | com.ctrip.framework.apollo
17 | spring-boot-dubbo-api
18 |
19 |
20 | com.alibaba.boot
21 | dubbo-spring-boot-starter
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter
26 |
27 |
28 | org.springframework.boot
29 | spring-boot-starter-logging
30 |
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-log4j2
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-consumer/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/starter/dubbo/consumer/Consumer.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.consumer;
2 |
3 | import com.alibaba.dubbo.config.annotation.Reference;
4 | import com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.api.DemoService;
5 | import java.io.BufferedReader;
6 | import java.io.InputStreamReader;
7 | import java.nio.charset.StandardCharsets;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.CommandLineRunner;
10 | import org.springframework.boot.WebApplicationType;
11 | import org.springframework.boot.autoconfigure.SpringBootApplication;
12 | import org.springframework.boot.builder.SpringApplicationBuilder;
13 | import org.springframework.stereotype.Component;
14 |
15 | /**
16 | * config the following properties in the spring-boot-dubbo-consumer app of apollo
17 | *
18 | * - dubbo.application.name = spring-boot-dubbo-consumer
19 | * - dubbo.registry.protocol = zookeeper
20 | * - dubbo.registry.address = 127.0.0.1:2181
21 | *
22 | */
23 | @SpringBootApplication
24 | public class Consumer implements CommandLineRunner {
25 |
26 | @Autowired
27 | private ConsumerService consumerService;
28 |
29 | public static void main(String[] args) throws Exception {
30 | new SpringApplicationBuilder(Consumer.class).web(WebApplicationType.NONE).run(args);
31 | }
32 |
33 | @Override
34 | public void run(String... args) throws Exception {
35 | System.out.println("Please input any key to test. Input quit to exit.");
36 | while (true) {
37 | System.out.print("> ");
38 | String input = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8)).readLine();
39 | if (input == null || input.length() == 0) {
40 | continue;
41 | }
42 | input = input.trim();
43 | if (input.equalsIgnoreCase("quit")) {
44 | System.exit(0);
45 | }
46 | System.out.println(consumerService.sayHello(input));
47 | }
48 | }
49 |
50 | @Component
51 | private static class ConsumerService {
52 |
53 | @Reference
54 | private DemoService demoService;
55 |
56 | public String sayHello(String message) {
57 | return demoService.sayHello(message);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-consumer/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-boot-dubbo-consumer
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 | # will inject 'application' namespace in bootstrap phase
5 | apollo.bootstrap.enabled=true
6 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-consumer/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-provider/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | spring-boot-dubbo
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-dubbo-provider
13 |
14 |
15 |
16 | com.ctrip.framework.apollo
17 | spring-boot-dubbo-api
18 |
19 |
20 | com.alibaba.boot
21 | dubbo-spring-boot-starter
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-provider/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/starter/dubbo/provider/DemoServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.provider;
2 |
3 | import com.alibaba.dubbo.config.annotation.Service;
4 | import com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.api.DemoService;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 |
8 | @Service
9 | public class DemoServiceImpl implements DemoService {
10 | private static final Logger LOGGER = LoggerFactory.getLogger(DemoServiceImpl.class);
11 |
12 | @Override
13 | public String sayHello(String name) {
14 | LOGGER.info("Say hello to {}", name);
15 | return "Hello " + name;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-provider/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/starter/dubbo/provider/Server.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.provider;
2 |
3 | import org.springframework.boot.WebApplicationType;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.builder.SpringApplicationBuilder;
6 |
7 | /**
8 | * config the following properties in the spring-boot-dubbo-provider app of apollo
9 | *
10 | * - dubbo.application.name = spring-boot-dubbo-provider
11 | * - dubbo.registry.protocol = zookeeper
12 | * - dubbo.registry.address = 127.0.0.1:2181
13 | * - dubbo.scan.basePackages = com.ctrip.framework.apollo.use.cases.spring.boot.starter.dubbo.provider
14 | *
15 | */
16 | @SpringBootApplication
17 | public class Server {
18 |
19 | public static void main(String[] args) throws Exception {
20 | new SpringApplicationBuilder(Server.class).web(WebApplicationType.NONE).run(args);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-provider/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-boot-dubbo-provider
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 | # will inject 'application' namespace in bootstrap phase
5 | apollo.bootstrap.enabled=true
6 | apollo.bootstrap.eagerLoad.enabled=true
7 |
--------------------------------------------------------------------------------
/spring-boot-dubbo/spring-boot-dubbo-provider/src/main/resources/logback-spring.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | [${logName}]%magenta([%thread]) %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %logger{36}.%M - %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/spring-boot-encrypt/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示如何结合[jasypt-spring-boot](https://github.com/ulisesbocchio/jasypt-spring-boot)实现Apollo中存储加密配置
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`spring-boot-encrypt`的项目
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | jasypt.encryptor.password = klklklklklklklkl
12 | test.input = ENC(Ore69lUopDHL5R8Bw/G3bQ==)
13 | test.input1 = ckl
14 | ```
15 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.boot.encrypt.Application`启动Demo
16 | 4. 程序会输出解密后的明文配置
17 | 5. 使用EncryptUtil小工具输出加密后的配置,加解密的keyjasypt.encryptor.password自己指定,添加配置时使用ENC()包含配置,如加密配置为xxx,则ENC(xxx)
18 |
--------------------------------------------------------------------------------
/spring-boot-encrypt/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-encrypt
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter
18 |
19 |
20 |
21 | com.github.ulisesbocchio
22 | jasypt-spring-boot-starter
23 | 1.16
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spring-boot-encrypt/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/encrypt/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.encrypt;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import java.util.concurrent.TimeUnit;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.boot.CommandLineRunner;
7 | import org.springframework.boot.SpringApplication;
8 | import org.springframework.boot.autoconfigure.SpringBootApplication;
9 |
10 | import javax.annotation.PostConstruct;
11 | import org.springframework.context.EnvironmentAware;
12 | import org.springframework.core.env.Environment;
13 |
14 | /**
15 | * Created by kl on 2018/6/25.
16 | * Content : 配置加解密实例
17 | */
18 | @SpringBootApplication
19 | @EnableApolloConfig
20 | public class Application implements CommandLineRunner, EnvironmentAware {
21 |
22 | public static void main(String[] args) {
23 | SpringApplication.run(Application.class, args);
24 | }
25 |
26 | @Value("${test.input}")
27 | private String input;
28 |
29 | @Value("${test.input1}")
30 | private String input1;
31 |
32 | private Environment environment;
33 |
34 | @Override
35 | public void run(String... args) throws Exception {
36 | while (true) {
37 | System.err.println("test.input 值 ENC(Ore69lUopDHL5R8Bw/G3bQ==) 解密后:" + input);
38 | System.err.println("test.input from environment: " + environment.getProperty("test.input"));
39 | System.err.println("test.input1 不需要解密:" + input1);
40 | TimeUnit.SECONDS.sleep(1);
41 | }
42 | }
43 |
44 | @Override
45 | public void setEnvironment(Environment environment) {
46 | this.environment = environment;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/spring-boot-encrypt/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/encrypt/EncryptUtil.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.encrypt;
2 |
3 | import com.ctrip.framework.apollo.core.utils.StringUtils;
4 | import org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI;
5 |
6 | import java.io.ByteArrayOutputStream;
7 | import java.io.PrintStream;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 | import java.util.regex.Matcher;
11 | import java.util.regex.Pattern;
12 |
13 | /**
14 | * Created by kl on 2018/6/25.
15 | * Content :加密工具
16 | */
17 | public class EncryptUtil {
18 |
19 | /**
20 | * 制表符、空格、换行符 PATTERN
21 | */
22 | private static Pattern BLANK_PATTERN = Pattern.compile("\\s*|\t|\r|\n");
23 |
24 | /**
25 | * 可以理解为加密salt
26 | */
27 | private static String PASSWORD = "klklklklklklklkl";
28 |
29 | /**
30 | * 加密算法
31 | */
32 | private static String ALGORITHM = "PBEWithMD5AndDES";
33 |
34 | public static Map getEncryptedParams(String input) {
35 | //输出流
36 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
37 | PrintStream cacheStream = new PrintStream(byteArrayOutputStream);
38 | //更换数据输出位置
39 | System.setOut(cacheStream);
40 |
41 | //加密参数组装
42 | String[] args = {"input=" + input, "password=" + PASSWORD, "algorithm=" + ALGORITHM};
43 | JasyptPBEStringEncryptionCLI.main(args);
44 |
45 | //执行加密后的输出
46 | String message = byteArrayOutputStream.toString();
47 | String str = replaceBlank(message);
48 | int index = str.lastIndexOf("-");
49 |
50 | //返回加密后的数据
51 | Map result = new HashMap();
52 | result.put("input", str.substring(index + 1));
53 | result.put("password", PASSWORD);
54 | return result;
55 | }
56 |
57 | public static void main(String[] args) {
58 | System.out.println(getEncryptedParams("kl"));//print : {input=Ore69lUopDHL5R8Bw/G3bQ==, password=klklklklklklklkl}
59 | }
60 |
61 | /**
62 | * 替换制表符、空格、换行符
63 | *
64 | * @param str
65 | * @return
66 | */
67 | private static String replaceBlank(String str) {
68 | String dest = "";
69 | if (!StringUtils.isEmpty(str)) {
70 | Matcher matcher = BLANK_PATTERN.matcher(str);
71 | dest = matcher.replaceAll("");
72 | }
73 | return dest;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/spring-boot-encrypt/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-boot-encrypt
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/spring-boot-logger/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Boot Logging](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html)如何通过Apollo配置中心实现动态调整Logging Level
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`spring-boot-logger`的项目
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | logging.level.com.ctrip.framework.apollo.use.cases.spring.boot.logger = warn
12 | ```
13 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.boot.logger.Application`启动Demo
14 | 4. 程序只会持续打印error级别日志
15 | 5. 在Apollo配置中心修改配置,把`logging.level.com.ctrip.framework.apollo.use.cases.spring.boot.logger`的值改为`debug`并发布配置
16 | 6. 程序会输出debug, info, warn, error等级别日志,说明动态调整Logging Level生效了
17 | 7. 更多信息可以参见博文:http://www.kailing.pub/article/index/arcid/189.html
18 |
--------------------------------------------------------------------------------
/spring-boot-logger/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-boot-logger
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spring-boot-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/logger/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.logger;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | /**
8 | * Created by kl on 2018/6/25.
9 | * Content :动态日志实例
10 | */
11 | @SpringBootApplication
12 | @EnableApolloConfig
13 | public class Application {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(Application.class, args);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-boot-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/logger/LoggerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.logger;
2 |
3 | import com.ctrip.framework.apollo.Config;
4 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
5 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
6 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
7 | import java.util.Set;
8 | import javax.annotation.PostConstruct;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.beans.factory.annotation.Autowired;
12 | import org.springframework.boot.logging.LogLevel;
13 | import org.springframework.boot.logging.LoggingSystem;
14 | import org.springframework.stereotype.Service;
15 |
16 | /**
17 | * Created by kl on 2018/6/25. Content :动态日志配置
18 | */
19 | @Service
20 | public class LoggerConfiguration {
21 | private static final Logger logger = LoggerFactory.getLogger(LoggerConfiguration.class);
22 | private static final String LOGGER_TAG = "logging.level.";
23 |
24 | @Autowired
25 | private LoggingSystem loggingSystem;
26 |
27 | @ApolloConfig
28 | private Config config;
29 |
30 | @ApolloConfigChangeListener
31 | private void onChange(ConfigChangeEvent changeEvent) {
32 | refreshLoggingLevels();
33 | }
34 |
35 | @PostConstruct
36 | private void refreshLoggingLevels() {
37 | Set keyNames = config.getPropertyNames();
38 | for (String key : keyNames) {
39 | if (containsIgnoreCase(key, LOGGER_TAG)) {
40 | String strLevel = config.getProperty(key, "info");
41 | LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());
42 | loggingSystem.setLogLevel(key.replace(LOGGER_TAG, ""), level);
43 | logger.info("{}:{}", key, strLevel);
44 | }
45 | }
46 | }
47 |
48 | private static boolean containsIgnoreCase(String str, String searchStr) {
49 | if (str == null || searchStr == null) {
50 | return false;
51 | }
52 | int len = searchStr.length();
53 | int max = str.length() - len;
54 | for (int i = 0; i <= max; i++) {
55 | if (str.regionMatches(true, i, searchStr, 0, len)) {
56 | return true;
57 | }
58 | }
59 | return false;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/spring-boot-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/boot/logger/PrintLogger.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.boot.logger;
2 |
3 | import java.util.concurrent.Executors;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.stereotype.Service;
7 |
8 | import javax.annotation.PostConstruct;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | * Created by kl on 2018/6/25.
13 | * Content :
14 | */
15 | @Service
16 | public class PrintLogger {
17 | private static Logger logger = LoggerFactory.getLogger(PrintLogger.class);
18 |
19 | @PostConstruct
20 | public void printLogger() throws Exception{
21 | Executors.newSingleThreadExecutor().submit(() -> {
22 | while (true) {
23 | logger.info("我是info级别日志");
24 | logger.error("我是error级别日志");
25 | logger.warn("我是warn级别日志");
26 | logger.debug("我是debug级别日志");
27 | TimeUnit.SECONDS.sleep(1);
28 | }
29 | });
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/spring-boot-logger/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-boot-logger
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/spring-cloud-gateway/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Cloud Gateway](https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.3.RELEASE/single/spring-cloud-gateway.html)如何通过Apollo配置中心实现动态路由
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`spring-cloud-Gateway`的项目
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | server.port = 9090
12 | spring.cloud.gateway.routes[0].id = github_route
13 | spring.cloud.gateway.routes[0].uri = https://github.com/
14 | #spring.cloud.gateway.routes[0].uri = https://github.com/ctripcorp/apollo
15 | spring.cloud.gateway.routes[0].predicates[0] = Path=/**
16 | ```
17 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.cloud.gateway.SpringCloudGatewayApplication`启动Demo
18 | 4. 打开`http://localhost:9090`,显式内容为GitHub首页
19 | 5. 在Apollo配置中心修改配置,把`spring.cloud.gateway.routes[0].uri`的值改为`https://github.com/ctripcorp/apollo`并发布配置
20 | * 可以以文本模式在原来生效的`spring.cloud.gateway.routes[0].uri`前面加上`#`注释掉,同时把原来注释掉的指向`https://github.com/ctripcorp/apollo`的配置反注释掉来快速修改
21 | 6. 刷新`http://localhost:9090`页面,显式的内容会变成Apollo配置中心的GitHub首页,说明动态路由生效了
22 | 7. 如果浏览器缓存影响测试,可以配置`actuator`使用`org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint`提供的端点查看路由配置
--------------------------------------------------------------------------------
/spring-cloud-gateway/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | apollo-use-cases
6 | com.ctrip.framework.apollo
7 | 1.0-SNAPSHOT
8 |
9 | 4.0.0
10 |
11 | spring-cloud-gateway
12 |
13 |
14 |
15 | org.springframework.cloud
16 | spring-cloud-starter-gateway
17 |
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter-test
22 | test
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spring-cloud-gateway/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/gateway/GatewayPropertiesRefresher.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.gateway;
2 |
3 | import com.ctrip.framework.apollo.enums.PropertyChangeType;
4 | import com.ctrip.framework.apollo.model.ConfigChange;
5 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
6 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.BeansException;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
12 | import org.springframework.cloud.gateway.config.GatewayProperties;
13 | import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
14 | import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
15 | import org.springframework.context.ApplicationContext;
16 | import org.springframework.context.ApplicationContextAware;
17 | import org.springframework.context.ApplicationEventPublisher;
18 | import org.springframework.context.ApplicationEventPublisherAware;
19 | import org.springframework.stereotype.Component;
20 |
21 | import java.util.ArrayList;
22 |
23 | /**
24 | * @author ksewen
25 | * @date 2019/5/175:24 PM
26 | */
27 | @Component
28 | public class GatewayPropertiesRefresher implements ApplicationContextAware,ApplicationEventPublisherAware {
29 |
30 | private static final Logger logger = LoggerFactory.getLogger(GatewayPropertiesRefresher.class);
31 |
32 | private static final String ID_PATTERN = "spring\\.cloud\\.gateway\\.routes\\[\\d+\\]\\.id";
33 |
34 | private static final String DEFAULT_FILTER_PATTERN = "spring\\.cloud\\.gateway\\.default-filters\\[\\d+\\]\\.name";
35 |
36 | private ApplicationContext applicationContext;
37 |
38 | private ApplicationEventPublisher publisher;
39 |
40 | @Autowired
41 | private GatewayProperties gatewayProperties;
42 |
43 | @Override
44 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
45 | this.applicationContext = applicationContext;
46 | }
47 |
48 |
49 | @Override
50 | public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
51 | this.publisher = applicationEventPublisher;
52 | }
53 |
54 | @ApolloConfigChangeListener(interestedKeyPrefixes = "spring.cloud.gateway.")
55 | public void onChange(ConfigChangeEvent changeEvent) {
56 | refreshGatewayProperties(changeEvent);
57 | }
58 |
59 | /***
60 | * 刷新org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator中定义的routes
61 | *
62 | * @param changeEvent
63 | * @return void
64 | * @author ksewen
65 | * @date 2019/5/21 2:13 PM
66 | */
67 | private void refreshGatewayProperties(ConfigChangeEvent changeEvent) {
68 | logger.info("Refreshing GatewayProperties!");
69 | preDestroyGatewayProperties(changeEvent);
70 | this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
71 | refreshGatewayRouteDefinition();
72 | logger.info("GatewayProperties refreshed!");
73 | }
74 |
75 | /***
76 | * GatewayProperties没有@PreDestroy和destroy方法
77 | * org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean时不会销毁当前对象
78 | * 如果把spring.cloud.gateway.前缀的配置项全部删除(例如需要动态删除最后一个路由的场景),initializeBean时也无法创建新的bean,则return当前bean
79 | * 若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean时会注入新的属性替换已有的bean
80 | * 这个方法提供了类似@PreDestroy的操作,根据配置文件的实际情况把org.springframework.cloud.gateway.config.GatewayProperties#routes
81 | * 和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters两个集合清空
82 | *
83 | * @param
84 | * @return void
85 | * @author ksewen
86 | * @date 2019/5/21 2:13 PM
87 | */
88 | private synchronized void preDestroyGatewayProperties(ConfigChangeEvent changeEvent) {
89 | logger.info("Pre Destroy GatewayProperties!");
90 | final boolean needClearRoutes = this.checkNeedClear(changeEvent, ID_PATTERN, this.gatewayProperties.getRoutes().size());
91 | if (needClearRoutes) {
92 | this.gatewayProperties.setRoutes(new ArrayList<>());
93 | }
94 | final boolean needClearDefaultFilters = this.checkNeedClear(changeEvent, DEFAULT_FILTER_PATTERN, this.gatewayProperties.getDefaultFilters().size());
95 | if (needClearDefaultFilters) {
96 | this.gatewayProperties.setDefaultFilters(new ArrayList<>());
97 | }
98 | logger.info("Pre Destroy GatewayProperties finished!");
99 | }
100 |
101 | private void refreshGatewayRouteDefinition() {
102 | logger.info("Refreshing Gateway RouteDefinition!");
103 | this.publisher.publishEvent(new RefreshRoutesEvent(this));
104 | logger.info("Gateway RouteDefinition refreshed!");
105 | }
106 |
107 | /***
108 | * 根据changeEvent和定义的pattern匹配key,如果所有对应PropertyChangeType为DELETED则需要清空GatewayProperties里相关集合
109 | *
110 | * @param changeEvent
111 | * @param pattern
112 | * @param existSize
113 | * @return boolean
114 | * @author ksewen
115 | * @date 2019/5/23 2:18 PM
116 | */
117 | private boolean checkNeedClear(ConfigChangeEvent changeEvent, String pattern, int existSize) {
118 |
119 | return changeEvent.changedKeys().stream().filter(key -> key.matches(pattern))
120 | .filter(key -> {
121 | ConfigChange change = changeEvent.getChange(key);
122 | return PropertyChangeType.DELETED.equals(change.getChangeType());
123 | }).count() == existSize;
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/spring-cloud-gateway/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/gateway/SpringCloudGatewayApplication.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.gateway;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | @EnableApolloConfig
8 | @SpringBootApplication
9 | public class SpringCloudGatewayApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(SpringCloudGatewayApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/spring-cloud-gateway/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-cloud-gateway
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
--------------------------------------------------------------------------------
/spring-cloud-logger/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Boot Logging](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html)在Spring Cloud环境下如何通过Apollo配置中心方便地实现动态调整Logging Level
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`spring-cloud-logger`的项目
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | logging.level.com.ctrip.framework.apollo.use.cases.spring.cloud.logger = warn
12 | ```
13 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.cloud.logger.Application`启动Demo
14 | 4. 程序只会持续打印error级别日志
15 | 5. 在Apollo配置中心修改配置,把`logging.level.com.ctrip.framework.apollo.use.cases.spring.cloud.logger`的值改为`debug`并发布配置
16 | 6. 程序会输出debug, info, warn, error等级别日志,说明动态调整Logging Level生效了
17 |
--------------------------------------------------------------------------------
/spring-cloud-logger/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-cloud-logger
13 |
14 |
15 |
16 | org.springframework.boot
17 | spring-boot-starter
18 |
19 |
20 | org.springframework.cloud
21 | spring-cloud-context
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spring-cloud-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/logger/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.logger;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 |
7 | /**
8 | * Created by kl on 2018/6/25.
9 | * Content :动态日志实例
10 | */
11 | @SpringBootApplication
12 | @EnableApolloConfig
13 | public class Application {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(Application.class, args);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/spring-cloud-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/logger/LoggerLevelRefresher.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.logger;
2 |
3 | import com.ctrip.framework.apollo.Config;
4 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
5 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
6 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
7 | import java.util.Set;
8 | import javax.annotation.PostConstruct;
9 | import org.springframework.beans.BeansException;
10 | import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
11 | import org.springframework.context.ApplicationContext;
12 | import org.springframework.context.ApplicationContextAware;
13 | import org.springframework.stereotype.Service;
14 |
15 | /**
16 | * Created by kl on 2018/6/25. Content :动态日志配置
17 | */
18 | @Service
19 | public class LoggerLevelRefresher implements ApplicationContextAware {
20 |
21 | private ApplicationContext applicationContext;
22 |
23 | @ApolloConfig
24 | private Config config;
25 |
26 | @PostConstruct
27 | private void initialize() {
28 | refreshLoggingLevels(config.getPropertyNames());
29 | }
30 |
31 | @ApolloConfigChangeListener(interestedKeyPrefixes = {"logging.level."})
32 | private void onChange(ConfigChangeEvent changeEvent) {
33 | refreshLoggingLevels(changeEvent.changedKeys());
34 | }
35 |
36 | private void refreshLoggingLevels(Set changedKeys) {
37 | System.out.println("Refreshing logging levels");
38 |
39 | /**
40 | * refresh logging levels
41 | * @see org.springframework.cloud.logging.LoggingRebinder#onApplicationEvent
42 | */
43 | this.applicationContext.publishEvent(new EnvironmentChangeEvent(changedKeys));
44 |
45 | System.out.println("Logging levels refreshed");
46 | }
47 |
48 | @Override
49 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
50 | this.applicationContext = applicationContext;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/spring-cloud-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/logger/PrintLogger.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.logger;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.TimeUnit;
5 | import javax.annotation.PostConstruct;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 | import org.springframework.stereotype.Service;
9 |
10 | /**
11 | * Created by kl on 2018/6/25.
12 | * Content :
13 | */
14 | @Service
15 | public class PrintLogger {
16 | private static Logger logger = LoggerFactory.getLogger(PrintLogger.class);
17 |
18 | @PostConstruct
19 | public void printLogger() throws Exception{
20 | Executors.newSingleThreadExecutor().submit(() -> {
21 | while (true) {
22 | logger.info("我是info级别日志");
23 | logger.error("我是error级别日志");
24 | logger.warn("我是warn级别日志");
25 | logger.debug("我是debug级别日志");
26 | TimeUnit.SECONDS.sleep(1);
27 | }
28 | });
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spring-cloud-logger/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-cloud-logger
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Cloud Zuul](https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#netflix-zuul-reverse-proxy)的第三方限流插件[marcosbarbero/spring-cloud-zuul-ratelimit](https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit)如何通过Apollo配置中心实现动态限流
4 |
5 | # Instructions
6 | 0. 启动本机redis或者手动修改对应配置
7 | 1. 在Apollo配置中心创建AppId为`spring-cloud-zuul-ratelimit`的项目,也可以沿用`spring-cloud-zuul`的项目(注意配置文件中`app.id`配置)
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | zuul.routes.test.path = /limit/**
12 | zuul.routes.test.url = forward:/index
13 | zuul.ratelimit.enabled = true
14 | zuul.ratelimit.repository = REDIS
15 | zuul.ratelimit.behind-proxy = true
16 | zuul.ratelimit.add-response-headers = true
17 | zuul.ratelimit.default-policy-list[0].limit = 0
18 | zuul.ratelimit.default-policy-list[0].quota = 1000
19 | zuul.ratelimit.default-policy-list[0].refresh-interval = 5
20 | zuul.ratelimit.default-policy-list[0].type[0] = user
21 | zuul.ratelimit.default-policy-list[0].type[1] = origin
22 | zuul.ratelimit.default-policy-list[0].type[2] = url
23 | # 通过实例配置覆盖默认配置,注意这里的`test`需要和网关对应路由的`test`关联
24 | # zuul.ratelimit.policy-list.test[0].limit = 1
25 | # zuul.ratelimit.policy-list.test[0].quota = 1000
26 | # zuul.ratelimit.policy-list.test[0].refresh-interval = 5
27 | # zuul.ratelimit.policy-list.test[0].type[0] = user
28 | # zuul.ratelimit.policy-list.test[0].type[1] = origin
29 | # zuul.ratelimit.policy-list.test[0].type[2] = url
30 | ```
31 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.cloud.zuul.Application`启动Demo
32 | 4. 手动打开`http://localhost:9090/limit`,页面显示`429`访问过载:
33 | 
34 | 5. 在Apollo配置中心修改配置,把`zuul.ratelimit.default-policy-list[0].limit`的值改为`1`并发布配置,再次访问,即可在5秒时间窗口内访问到1次`http://localhost:9090/limit`端点,说明动态路由生效了
35 | * 更详细的限流配置,请看[marcosbarbero/spring-cloud-zuul-ratelimit](https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit)或者这里有一篇快速入门[Spring Cloud 入门教程9、服务限流/API限流(Zuul+RateLimiter)](https://ken.io/note/spring-cloud-zuul-ratelimiter-quickstart)
36 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-cloud-zuul-ratelimit
13 |
14 |
15 |
16 |
17 | org.springframework.cloud
18 | spring-cloud-starter-netflix-zuul
19 |
20 |
21 | org.springframework.boot
22 | spring-boot-starter
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-starter-logging
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-log4j2
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | com.marcosbarbero.cloud
43 | spring-cloud-zuul-ratelimit
44 |
45 |
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-data-redis
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/zuul/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.zuul;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.builder.SpringApplicationBuilder;
7 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
8 | import org.springframework.web.bind.annotation.GetMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @EnableApolloConfig
12 | @EnableZuulProxy
13 | @SpringBootApplication
14 | @RestController
15 | public class Application {
16 |
17 | @Value("${server.port}")
18 | private int port;
19 |
20 | public static void main(String[] args) throws Exception {
21 | new SpringApplicationBuilder(Application.class).run(args);
22 | }
23 |
24 | @GetMapping("/index")
25 | public String index() {
26 | return String.format("index %s", port);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/zuul/ZuulRateLimitPropertiesRefreshConfig.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.zuul;
2 |
3 | import static com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitProperties.PREFIX;
4 |
5 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
6 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.beans.BeansException;
10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
11 | import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
12 | import org.springframework.context.ApplicationContext;
13 | import org.springframework.context.ApplicationContextAware;
14 | import org.springframework.stereotype.Component;
15 |
16 | @Component
17 | @ConditionalOnProperty(prefix = PREFIX, name = "enabled", havingValue = "true")
18 | public class ZuulRateLimitPropertiesRefreshConfig implements ApplicationContextAware {
19 |
20 | private static final Logger logger = LoggerFactory.getLogger(ZuulRateLimitPropertiesRefreshConfig.class);
21 |
22 | private ApplicationContext applicationContext;
23 |
24 | @ApolloConfigChangeListener(interestedKeyPrefixes = PREFIX)
25 | public void onChange(ConfigChangeEvent changeEvent) {
26 | logger.info("Refreshing Zuul rateLimit Properties");
27 |
28 | this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
29 |
30 | logger.info("Zuul rateLimit Properties refreshed!");
31 | }
32 |
33 | @Override
34 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
35 | this.applicationContext = applicationContext;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-cloud-zuul-ratelimit
2 | apollo.meta=http://localhost:8080
3 | apollo.bootstrap.enabled=true
4 | server.port=9090
5 | spring.redis.host=localhost
6 | spring.redis.port=6379
7 |
8 | # 如需要可以配置actuator端点,进行手动刷新,非必须
9 | #management.endpoints.web.exposure.include=refresh,health
10 | #management.endpoints.web.exposure.include=*
11 | #management.endpoint.health.show-details=ALWAYS
12 |
--------------------------------------------------------------------------------
/spring-cloud-zuul-ratelimit/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/README.md:
--------------------------------------------------------------------------------
1 | # Purpose
2 |
3 | 演示[Spring Cloud Zuul](https://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#netflix-zuul-reverse-proxy)如何通过Apollo配置中心实现动态路由
4 |
5 | # Instructions
6 |
7 | 1. 在Apollo配置中心创建AppId为`spring-cloud-zuul`的项目
8 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
9 |
10 | ```properties
11 | server.port = 9090
12 | zuul.routes.test.path = /**
13 | zuul.routes.test.url = https://github.com
14 | #zuul.routes.test.url = https://github.com/ctripcorp/apollo
15 | ```
16 | 3. 运行`com.ctrip.framework.apollo.use.cases.spring.cloud.zuul.Application`启动Demo
17 | 4. 程序会自动打开`http://localhost:9090`,显式内容为GitHub首页
18 | 5. 在Apollo配置中心修改配置,把`zuul.routes.test.url`的值改为`https://github.com/ctripcorp/apollo`并发布配置
19 | * 可以以文本模式在原来生效的`zuul.routes.test.url`前面加上`#`注释掉,同时把原来注释掉的指向`https://github.com/ctripcorp/apollo`的配置反注释掉来快速修改
20 | 6. 刷新`http://localhost:9090`页面,显式的内容会变成Apollo配置中心的GitHub首页,说明动态路由生效了
21 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-cloud-zuul
13 |
14 |
15 |
16 | org.springframework.cloud
17 | spring-cloud-starter-netflix-zuul
18 |
19 |
20 | org.springframework.boot
21 | spring-boot-starter
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-logging
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-log4j2
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/zuul/Application.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.zuul;
2 |
3 | import com.ctrip.framework.apollo.spring.annotation.EnableApolloConfig;
4 | import java.awt.Desktop;
5 | import java.net.URI;
6 | import org.springframework.boot.autoconfigure.SpringBootApplication;
7 | import org.springframework.boot.builder.SpringApplicationBuilder;
8 | import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
9 | import org.springframework.context.ApplicationContext;
10 |
11 | @EnableApolloConfig
12 | @EnableZuulProxy
13 | @SpringBootApplication
14 | public class Application {
15 |
16 | public static void main(String[] args) throws Exception {
17 | System.setProperty("java.awt.headless", "false");
18 | ApplicationContext context = new SpringApplicationBuilder(Application.class).run(args);
19 | Desktop.getDesktop().browse(
20 | new URI("http://localhost:" + context.getEnvironment().getProperty("server.port", "8080")));
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/src/main/java/com/ctrip/framework/apollo/use/cases/spring/cloud/zuul/ZuulPropertiesRefresher.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.cloud.zuul;
2 |
3 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
4 | import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.beans.BeansException;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
10 | import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
11 | import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
12 | import org.springframework.context.ApplicationContext;
13 | import org.springframework.context.ApplicationContextAware;
14 | import org.springframework.stereotype.Component;
15 |
16 | @Component
17 | public class ZuulPropertiesRefresher implements ApplicationContextAware {
18 |
19 | private static final Logger logger = LoggerFactory.getLogger(ZuulPropertiesRefresher.class);
20 |
21 | private ApplicationContext applicationContext;
22 |
23 | @Autowired
24 | private RouteLocator routeLocator;
25 |
26 | @ApolloConfigChangeListener(interestedKeyPrefixes = "zuul.")
27 | public void onChange(ConfigChangeEvent changeEvent) {
28 | refreshZuulProperties(changeEvent);
29 | }
30 |
31 | private void refreshZuulProperties(ConfigChangeEvent changeEvent) {
32 | logger.info("Refreshing zuul properties!");
33 |
34 | /**
35 | * rebind configuration beans, e.g. ZuulProperties
36 | * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent
37 | */
38 | this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
39 |
40 | /**
41 | * refresh routes
42 | * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent
43 | */
44 | this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator));
45 |
46 | logger.info("Zuul properties refreshed!");
47 | }
48 |
49 | @Override
50 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
51 | this.applicationContext = applicationContext;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-cloud-zuul
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/spring-cloud-zuul/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/spring-mvc-logger/README.md:
--------------------------------------------------------------------------------
1 | # Instructions
2 |
3 | 1. 在Apollo配置中心创建AppId为`spring-mvc-logger`的项目
4 | 2. 在默认的`application`下做如下配置(可以通过文本模式直接复制、粘贴下面的内容):
5 |
6 | ```
7 | apollo.setting.app.name = spring-mvc-logger
8 | ```
9 | 3. 在项目中的LoggerStartupListener监听器中设置需要动态更新的值`appname`,并且在logback.xml中引用`${appname}`
10 | 4. 用tomcat启动`spring-mvc-logger`项目
11 | 5. 可以看到打印日志:
12 | ```
13 | [app_name=spring-mvc-logger][timestamp=2021-03-20 13:34:45.406][level=INFO][msg=the value of the logback field from apollo, apollo.setting.app.name is spring-mvc-logger]
14 | ```
15 | 5. 在Apollo配置中心修改配置,把`apollo.setting.app.name`的值改为`newvalue`并发布配置
16 | 6. 可以看到打印日志已更新:
17 | ```
18 | [app_name=newvalue][timestamp=2021-03-20 13:38:23.928][level=INFO][msg=reload loggerContext , you can see that the log has been updated, new value from apollo is newvalue]
19 | ```
20 | 说明logback.xml中app_name的值随着apollo配置的更新而动态更新了
21 | 7. 更多信息可以参见:https://github.com/ctripcorp/apollo/issues/2482#issuecomment-801901167
22 |
--------------------------------------------------------------------------------
/spring-mvc-logger/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | apollo-use-cases
7 | com.ctrip.framework.apollo
8 | 1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | spring-mvc-logger
13 |
14 |
15 | 4.3.4.RELEASE
16 |
17 |
18 |
19 | org.projectlombok
20 | lombok
21 | 1.18.16
22 |
23 |
24 | ch.qos.logback
25 | logback-classic
26 | 1.2.3
27 |
28 |
29 | org.springframework
30 | spring-context
31 | ${spring-version}
32 |
33 |
34 |
35 | org.springframework
36 | spring-web
37 | ${spring-version}
38 |
39 |
40 | org.springframework
41 | spring-webmvc
42 | ${spring-version}
43 |
44 |
45 | org.springframework
46 | spring-test
47 | test
48 | ${spring-version}
49 |
50 |
51 |
52 |
53 | spring-mvc-logger
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-compiler-plugin
58 | 2.5.1
59 |
60 | 1.8
61 | 1.8
62 | UTF8
63 |
64 |
65 |
66 | org.apache.tomcat.maven
67 | tomcat7-maven-plugin
68 | 2.2
69 |
70 | 80
71 | /
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/spring-mvc-logger/src/main/java/com/ctrip/framework/apollo/use/cases/spring/mvc/logger/LoggerStartupListener.java:
--------------------------------------------------------------------------------
1 | package com.ctrip.framework.apollo.use.cases.spring.mvc.logger;
2 |
3 | import ch.qos.logback.classic.Level;
4 | import ch.qos.logback.classic.Logger;
5 | import ch.qos.logback.classic.LoggerContext;
6 | import ch.qos.logback.classic.spi.LoggerContextListener;
7 | import ch.qos.logback.classic.util.ContextInitializer;
8 | import ch.qos.logback.core.Context;
9 | import ch.qos.logback.core.joran.spi.JoranException;
10 | import ch.qos.logback.core.spi.ContextAwareBase;
11 | import ch.qos.logback.core.spi.LifeCycle;
12 | import com.ctrip.framework.apollo.Config;
13 | import com.ctrip.framework.apollo.ConfigChangeListener;
14 | import com.ctrip.framework.apollo.ConfigService;
15 | import com.ctrip.framework.apollo.model.ConfigChange;
16 | import com.ctrip.framework.apollo.model.ConfigChangeEvent;
17 | import org.slf4j.LoggerFactory;
18 | import org.springframework.stereotype.Component;
19 |
20 | import java.net.URL;
21 | import java.util.concurrent.atomic.AtomicBoolean;
22 |
23 | /**
24 | * @author bugcoder
25 | * @date 2021/3/17
26 | */
27 | @Component
28 | public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
29 |
30 | private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggerStartupListener.class);
31 | private static AtomicBoolean started = new AtomicBoolean(false);
32 | private static String APOLLO_SETTING_NAME = "apollo.setting.app.name";
33 | private static String LOGBACK_SETTING_NAME = "appname";
34 |
35 | @Override
36 | public void start() {
37 | if (started.compareAndSet(false,true)){
38 | //从apollo中获取所有配置信息
39 | Config config = ConfigService.getAppConfig();
40 | String apolloValue = config.getProperty(APOLLO_SETTING_NAME, "这里设置默认值");
41 | Context context = getContext();
42 | //ConfigChangeListener用来监听apollo配置的变化
43 | config.addChangeListener(new ConfigChangeListener() {
44 | @Override
45 | public void onChange(ConfigChangeEvent configChangeEvent) {
46 | for (String key : configChangeEvent.changedKeys()) {
47 | if (APOLLO_SETTING_NAME.equals(key)){
48 | ConfigChange change = configChangeEvent.getChange(key);
49 | reloadDefaultConfiguration(change);
50 | }
51 |
52 | }
53 | }
54 | });
55 | context.putProperty(LOGBACK_SETTING_NAME, apolloValue);
56 | log.info("the value of the logback field from apollo, apollo.setting.app.name is {}", apolloValue);
57 | }
58 | }
59 |
60 | @Override
61 | public void stop() {
62 | }
63 |
64 | @Override
65 | public boolean isStarted() {
66 | return started.get();
67 | }
68 |
69 | @Override
70 | public boolean isResetResistant() {
71 | return true;
72 | }
73 |
74 | @Override
75 | public void onStart(LoggerContext context) {
76 |
77 | }
78 |
79 | @Override
80 | public void onReset(LoggerContext context) {
81 |
82 | }
83 |
84 | @Override
85 | public void onStop(LoggerContext context) {
86 |
87 | }
88 |
89 | @Override
90 | public void onLevelChange(Logger logger, Level level) {
91 |
92 | }
93 |
94 | /**
95 | * 重新加载logback, 并更新logback字段的值
96 | * @param change 这里面包含了apollo的变化值
97 | */
98 | private void reloadDefaultConfiguration(ConfigChange change) {
99 | LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
100 | ContextInitializer ci = new ContextInitializer(loggerContext);
101 | URL url = ci.findURLOfDefaultConfigurationFile(true);
102 | loggerContext.reset();
103 | loggerContext.putProperty(LOGBACK_SETTING_NAME,change.getNewValue());
104 | try {
105 | ci.configureByResource(url);
106 | } catch (JoranException e) {
107 | e.printStackTrace();
108 | }
109 | log.info("reload loggerContext , you can see that the log has been updated, new value from apollo is {}",change.getNewValue());
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/spring-mvc-logger/src/main/resources/META-INF/app.properties:
--------------------------------------------------------------------------------
1 | app.id=spring-mvc-logger
2 | # set apollo meta server address, adjust to actual address if necessary
3 | apollo.meta=http://localhost:8080
4 |
--------------------------------------------------------------------------------
/spring-mvc-logger/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | ${logPattern}
17 |
18 |
19 |
20 |
21 | ${logDir}/debug.log
22 |
23 | DEBUG
24 | ACCEPT
25 | DENY
26 |
27 |
28 | ${logDir}/debug/debuglog.%d{yyyyMMdd}.log.gz
29 | ${maxHistory}
30 |
31 |
32 | ${logPattern}
33 |
34 |
35 |
36 |
37 |
38 | ${logDir}/info.log
39 |
40 | INFO
41 | ACCEPT
42 | DENY
43 |
44 |
45 | ${logDir}/info/infolog.%d{yyyyMMdd}.log.gz
46 | ${maxHistory}
47 |
48 |
49 | ${logPattern}
50 |
51 |
52 |
53 |
54 |
55 | ${logDir}/warn.log
56 |
57 | WARN
58 | ACCEPT
59 | DENY
60 |
61 |
62 | ${logDir}/warn/warnlog.%d{yyyyMMdd}.log.gz
63 | ${maxHistory}
64 |
65 |
66 | ${logPattern}
67 |
68 |
69 |
70 |
71 |
72 | ${logDir}/error.log
73 |
74 | ERROR
75 | ACCEPT
76 | DENY
77 |
78 |
79 | ${logDir}/error/errorlog.%d{yyyyMMdd}.log.gz
80 | ${maxHistory}
81 |
82 |
83 | ${logPattern}
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/spring-mvc-logger/src/main/resources/spring-web.xml:
--------------------------------------------------------------------------------
1 |
2 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/spring-mvc-logger/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | dispatcherServlet
9 | org.springframework.web.servlet.DispatcherServlet
10 |
11 | contextConfigLocation
12 | classpath:spring-*.xml
13 |
14 | 1
15 |
16 |
17 |
18 | dispatcherServlet
19 | /
20 |
21 |
22 |
--------------------------------------------------------------------------------