参考:appollo官方
下载源码
-Denv=dev
-Dspring.profiles.active=github
-Deureka.service.url=http://localhost:8080/eureka/
-Dspring.datasource.url=jdbc:mysql://localhost:3309/ApolloConfigDB?characterEncoding=utf8
-Dspring.datasource.username=root
-Dspring.datasource.password=123456
-Dlogging.file=D:/logs/apollo-assembly.log
–configservice --adminservice
这些配置其实可以参考官方的项目apollo-build-scripts项目
这个项目有一个deom.sh脚本如下:不需要完全看懂所有意思,只需看重点如下:
# meta server url定义的一些变量
config_server_url=http://localhost:8080
admin_server_url=http://localhost:8090
eureka_service_url=$config_server_url/eureka/
portal_url=http://localhost:8070
# JAVA OPTS BASE_JAVA_OPTS:基础变量
BASE_JAVA_OPTS="-Denv=dev"
CLIENT_JAVA_OPTS="$BASE_JAVA_OPTS -Dapollo.meta=$config_server_url"
#config service和admin service所需的VM options配置参数
SERVER_JAVA_OPTS="$BASE_JAVA_OPTS -Dspring.profiles.active=github -Deureka.service.url=$eureka_service_url"
#portal service所需的VM options配置参数
PORTAL_JAVA_OPTS="$BASE_JAVA_OPTS -Ddev_meta=$config_server_url -Dspring.profiles.active=github,auth -Deureka.client.enabled=false -Dhibernate.query.plan_cache_max_size=192"
#启动config service和admin service时所带的Program arguments参数
$SERVICE_JAR start --configservice --adminservice
注意apollo-assembly这个项目是将config 和admin一起启动的, 并且是先启动config 再启动admin。因为config这个服务中整合了eureka 服务端,所以要先把eureka服务启动,然后config 和admin再注册到eureka。
注:在启动apollo-configservice的过程中会在日志中输出eureka注册失败的信息,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused。需要注意的是,这个是预期的情况,因为apollo-configservice需要向Meta Server(它自己)注册服务,但是因为在启动过程中,自己还没起来,所以会报这个错。后面会进行重试的动作,所以等自己服务起来后就会注册正常了。
-Denv=dev
-Ddev_meta=http://localhost:8080
-Dspring.profiles.active=github,auth
-Deureka.client.enabled=false
-Dhibernate.query.plan_cache_max_size=192
-Dserver.port=8170
-Dspring.datasource.url=jdbc:mysql://localhost:3309/ApolloPortalDB?characterEncoding=utf8
-Dspring.datasource.username=root
-Dspring.datasource.password=123456
-Dlogging.file=D:/logsh/apollo-portal.log
–portal
这些配置也同样参考官方的项目apollo-build-scripts项目的deom.sh脚本如下:不需要完全看懂所有意思,只需看重点如下:
#portal service所需的VM options配置参数
PORTAL_JAVA_OPTS="$BASE_JAVA_OPTS -Ddev_meta=$config_server_url -Dspring.profiles.active=github,auth -Deureka.client.enabled=false -Dhibernate.query.plan_cache_max_size=192"
echo "==== starting portal ===="
echo "Portal logging file is $PORTAL_LOG"
# 导入VM options配置参数
export JAVA_OPTS="$PORTAL_JAVA_OPTS -Dlogging.file=./apollo-portal.log -Dserver.port=8070 -Dspring.datasource.url=$apollo_portal_db_url -Dspring.datasource.username=$apollo_portal_db_username -Dspring.datasource.password=$apollo_portal_db_password"
# 启动portal service 的参数
$PORTAL_JAR start --portal
Admin Service发送ReleaseMessage方式:
1、 Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender
2、Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner
3、Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration
4、NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端
deferredResults 对象被定成NotificationControllerV2类的成员变量。
客户端会发起一个长连接到config service服务,url对应了NotificationControllerV2.#pollNotification 方法,并返回DeferredResult对象, 返回这个对象 ,那么请求并不会立即返回,直到DeferredResult.setResult 被设置值 ,请求才会返回。
那么这个对象DeferredResult.setResult 何时调用?
@RestController
@RequestMapping("/notifications/v2")
public class NotificationControllerV2 implements ReleaseMessageListener {
//定义deferredResults 成员变量,设置成同步集合,防止并发
//DeferredResultWrapper类型是DeferredResult的包装类,里面持有DeferredResult类型的变量
private final Multimap<String, DeferredResultWrapper> deferredResults =
Multimaps.synchronizedSetMultimap(TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural()));
}
//客户端定时获取 最新配置url方法
@GetMapping
public DeferredResult<ResponseEntity<List<ApolloConfigNotification>>> pollNotification(
@RequestParam(value = "appId") String appId,
@RequestParam(value = "cluster") String cluster,
@RequestParam(value = "notifications") String notificationsAsString,
@RequestParam(value = "dataCenter", required = false) String dataCenter,
@RequestParam(value = "ip", required = false) String clientIp) {
// 解析 notificationsAsString 参数,创建 ApolloConfigNotification 数组。
List<ApolloConfigNotification> notifications = null;
//创建deferredResultWrapper 对象
DeferredResultWrapper deferredResultWrapper = new DeferredResultWrapper(bizConfig.longPollingTimeoutInMilli());
deferredResultWrapper.onCompletion(() -> {
// 当这个对象resultWrapper完成setReuslt设置, 注销已经返回的监听
for (String key : watchedKeys) {
deferredResults.remove(key, deferredResultWrapper);
}
});
//注册DeferredResultWrapper 到 `deferredResults` 中,等待配置发生变化后通知。详见 `#handleMessage(...)` 方法。
for (String key : watchedKeys) {
this.deferredResults.put(key, deferredResultWrapper);
}
//返回DeferredResult对象,直到#handleMessage监听方法中设置了值返回
//或等待超时返回304
return deferredResultWrapper.getResult();
}
//配置发布后的通知方法
@Override
public void handleMessage(ReleaseMessage message, String channel) {
logger.info("message received - channel: {}, message: {}", channel, message);
List<DeferredResultWrapper> results = Lists.newArrayList(deferredResults.get(content));
if (results.size() > bizConfig.releaseMessageNotificationBatch()) {
largeNotificationBatchExecutorService.submit(() -> {
logger.debug("Async notify {} clients for key {} with batch {}", results.size(), content,
bizConfig.releaseMessageNotificationBatch());
for (int i = 0; i < results.size(); i++) {
//如果有配置更新,那么调用了DeferredResultWrapper类对象的setResult方法,
//这个方法就是获取内部的成量DeferredResult 并设置值。
results.get(i).setResult(configNotification);
}
});
return;
}
}