AppBootstrap.java 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. package cn.com.qmth.examcloud.web.bootstrap;
  2. import java.util.Map;
  3. import java.util.Map.Entry;
  4. import java.util.Properties;
  5. import java.util.Set;
  6. import org.apache.commons.io.IOUtils;
  7. import org.apache.commons.lang3.StringUtils;
  8. import org.apache.logging.log4j.ThreadContext;
  9. import org.springframework.boot.SpringApplication;
  10. import org.springframework.context.ConfigurableApplicationContext;
  11. import org.springframework.http.HttpStatus;
  12. import com.google.common.collect.Maps;
  13. import com.google.common.collect.Sets;
  14. import cn.com.qmth.examcloud.commons.exception.ExamCloudRuntimeException;
  15. import cn.com.qmth.examcloud.commons.logging.ExamCloudLog;
  16. import cn.com.qmth.examcloud.commons.logging.ExamCloudLogFactory;
  17. import cn.com.qmth.examcloud.commons.util.HttpMethod;
  18. import cn.com.qmth.examcloud.commons.util.JsonUtil;
  19. import cn.com.qmth.examcloud.commons.util.OKHttpUtil;
  20. import cn.com.qmth.examcloud.commons.util.PropertiesUtil;
  21. import cn.com.qmth.examcloud.commons.util.StringUtil;
  22. import cn.com.qmth.examcloud.commons.util.Util;
  23. import cn.com.qmth.examcloud.web.cloud.AppSelf;
  24. import cn.com.qmth.examcloud.web.cloud.AppSelfHolder;
  25. import okhttp3.Response;
  26. /**
  27. * 系统启动器
  28. *
  29. * @author WANGWEI
  30. * @date 2019年3月25日
  31. * @Copyright (c) 2018-? http://qmth.com.cn All Rights Reserved.
  32. */
  33. public class AppBootstrap {
  34. private static ExamCloudLog log = ExamCloudLogFactory.getLog(AppBootstrap.class);
  35. private static Map<String, String> properties;
  36. private static String active;
  37. private static String startupCode;
  38. private static Long appId;
  39. private static String secretKey;
  40. private static String appCode;
  41. private static String configCenterHost;
  42. private static String configCenterPort;
  43. /**
  44. * 启动方法
  45. *
  46. * @author WANGWEI
  47. * @param primarySource
  48. * @param args
  49. * @return
  50. */
  51. public static synchronized ConfigurableApplicationContext run(Class<?> primarySource,
  52. String... args) {
  53. ThreadContext.put("TRACE_ID", Thread.currentThread().getName());
  54. log.info("Starting...");
  55. checkBootstrapParams(args);
  56. sendStartupRequest2ConfigCenter();
  57. if (null != args) {
  58. for (String arg : args) {
  59. arg = arg.trim();
  60. if (arg.startsWith("--")) {
  61. String key = arg.substring(2, arg.indexOf("=")).trim();
  62. String value = arg.substring(arg.indexOf("=") + 1).trim();
  63. properties.put(key, value);
  64. }
  65. }
  66. }
  67. // 网卡选择
  68. String preferredNetworks = properties.get("examcloud.inet.preferredNetworks");
  69. if (StringUtils.isNotBlank(preferredNetworks)) {
  70. System.setProperty("spring.cloud.inetutils.preferred-networks", preferredNetworks);
  71. properties.put("spring.cloud.inetutils.preferred-networks", preferredNetworks);
  72. }
  73. properties.put("spring.profiles.active", active);
  74. Set<String> argSet = Sets.newLinkedHashSet();
  75. for (Entry<String, String> p : properties.entrySet()) {
  76. String key = p.getKey();
  77. String value = p.getValue();
  78. if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
  79. continue;
  80. }
  81. key = key.trim();
  82. value = value.trim();
  83. PropertyHolder.setProperty(key, value);
  84. PropertiesUtil.setProperty(key, value);
  85. String arg = "--" + key + "=" + value;
  86. argSet.add(arg);
  87. }
  88. AppSelfHolder.set(buildAppSelf());
  89. String[] newArgs = argSet.toArray(new String[argSet.size()]);
  90. ConfigurableApplicationContext context = null;
  91. try {
  92. context = SpringApplication.run(primarySource, newArgs);
  93. noticeConfigCenter("success");
  94. } catch (Exception e) {
  95. String stackTrace = Util.getStackTrace(e);
  96. noticeConfigCenter("failure:\n" + stackTrace);
  97. log.error("fail to run spring app.", e);
  98. System.exit(-1);
  99. }
  100. return context;
  101. }
  102. /**
  103. * 创建 {@link AppSelf} 实例
  104. *
  105. * @author WANGWEI
  106. * @return
  107. */
  108. private static AppSelf buildAppSelf() {
  109. return new AppSelf() {
  110. @Override
  111. public Long getAppId() {
  112. return appId;
  113. }
  114. @Override
  115. public String getAppCode() {
  116. return appCode;
  117. }
  118. @Override
  119. public String getSecretKey() {
  120. return secretKey;
  121. }
  122. };
  123. }
  124. /**
  125. * 通知配置中心
  126. *
  127. * @author WANGWEI
  128. * @param message
  129. */
  130. private static void noticeConfigCenter(String message) {
  131. String url = "http://" + configCenterHost + ":" + configCenterPort + "/configCenter/notice";
  132. Map<String, String> req = Maps.newHashMap();
  133. req.put("active", active);
  134. req.put("appCode", appCode);
  135. req.put("startupCode", startupCode);
  136. req.put("message", message);
  137. Response resp = null;
  138. try {
  139. Map<String, String> headers = Maps.newHashMap();
  140. headers.put("Trace-Id", startupCode);
  141. resp = OKHttpUtil.call(HttpMethod.POST, url.toString(), headers, req);
  142. } catch (Exception e) {
  143. log.error("fail to notice config center.", e);
  144. System.exit(-1);
  145. } finally {
  146. IOUtils.closeQuietly(resp);
  147. }
  148. }
  149. /**
  150. * 向配置中心发送启动请求
  151. *
  152. * @author WANGWEI
  153. */
  154. private static void sendStartupRequest2ConfigCenter() {
  155. StringBuilder url = new StringBuilder(
  156. "http://" + configCenterHost + ":" + configCenterPort + "/configCenter/startup");
  157. Map<String, String> req = Maps.newHashMap();
  158. req.put("active", active);
  159. req.put("appCode", appCode);
  160. req.put("startupCode", startupCode);
  161. Response resp = null;
  162. try {
  163. Map<String, String> headers = Maps.newHashMap();
  164. headers.put("Trace-Id", startupCode);
  165. resp = OKHttpUtil.call(HttpMethod.POST, url.toString(), headers, req);
  166. if (resp.code() != HttpStatus.OK.value()) {
  167. throw new ExamCloudRuntimeException("fail to get configuration");
  168. }
  169. String body = resp.body().string();
  170. String encryptedAppId = resp.header("App-Id");
  171. String encryptedSecretKey = resp.header("Secret-Key");
  172. appId = StringUtil.toLong(BootstrapSecurityUtil.decrypt(encryptedAppId, startupCode));
  173. secretKey = BootstrapSecurityUtil.decrypt(encryptedSecretKey, startupCode);
  174. String decryptedBody = BootstrapSecurityUtil.decrypt(body, startupCode);
  175. @SuppressWarnings("unchecked")
  176. Map<String, String> map = JsonUtil.fromJson(decryptedBody, Map.class);
  177. properties = map;
  178. } catch (Exception e) {
  179. log.error("fail to send startup request to config center.", e);
  180. System.exit(-1);
  181. } finally {
  182. IOUtils.closeQuietly(resp);
  183. }
  184. }
  185. /**
  186. * 检查启动参数
  187. *
  188. * @author WANGWEI
  189. * @param args
  190. * @return
  191. */
  192. private static void checkBootstrapParams(String... args) {
  193. Properties props = new Properties();
  194. PropertiesUtil.loadFromResource("application.properties", props);
  195. if (null != args) {
  196. for (String s : args) {
  197. s = s.trim();
  198. if (s.startsWith("--spring.profiles.active=")) {
  199. active = s.substring(s.indexOf("=") + 1);
  200. } else if (s.startsWith("--examcloud.startup.startupCode=")) {
  201. startupCode = s.substring(s.indexOf("=") + 1);
  202. } else if (s.startsWith("--examcloud.startup.configCenterHost=")) {
  203. configCenterHost = s.substring(s.indexOf("=") + 1);
  204. } else if (s.startsWith("--examcloud.startup.configCenterPort=")) {
  205. configCenterPort = s.substring(s.indexOf("=") + 1);
  206. } else if (s.startsWith("--examcloud.startup.appCode=")) {
  207. appCode = s.substring(s.indexOf("=") + 1);
  208. }
  209. }
  210. }
  211. // active
  212. if (null == active) {
  213. String value = props.getProperty("spring.profiles.active");
  214. if (StringUtils.isNotBlank(value)) {
  215. active = value.trim();
  216. }
  217. }
  218. log.info("active=" + active);
  219. if (StringUtils.isBlank(active)) {
  220. log.error("property[spring.profiles.active] is not specified");
  221. System.exit(-1);
  222. }
  223. // startupCode
  224. if (null == startupCode) {
  225. String value = props.getProperty("examcloud.startup.startupCode");
  226. if (StringUtils.isNotBlank(value)) {
  227. startupCode = value.trim();
  228. }
  229. }
  230. log.info("startupCode=" + startupCode);
  231. if (StringUtils.isBlank(startupCode)) {
  232. log.error("property[examcloud.startup.startupCode] is not specified");
  233. System.exit(-1);
  234. }
  235. // appCode
  236. if (null == appCode) {
  237. String value = props.getProperty("examcloud.startup.appCode");
  238. if (StringUtils.isNotBlank(value)) {
  239. appCode = value.trim();
  240. }
  241. }
  242. log.info("appCode=" + appCode);
  243. if (StringUtils.isBlank(appCode)) {
  244. log.error("property[examcloud.startup.appCode] is not specified");
  245. System.exit(-1);
  246. }
  247. // configCenterHost
  248. if (null == configCenterHost) {
  249. String value = props.getProperty("examcloud.startup.configCenterHost");
  250. if (StringUtils.isNotBlank(value)) {
  251. configCenterHost = value.trim();
  252. }
  253. }
  254. log.info("configCenterHost=" + configCenterHost);
  255. if (StringUtils.isBlank(configCenterHost)) {
  256. log.error("property[examcloud.startup.configCenterHost] is not specified");
  257. System.exit(-1);
  258. }
  259. // configCenterPort
  260. if (null == configCenterPort) {
  261. String value = props.getProperty("examcloud.startup.configCenterPort");
  262. if (StringUtils.isNotBlank(value)) {
  263. configCenterPort = value.trim();
  264. }
  265. }
  266. log.info("configCenterPort=" + configCenterPort);
  267. if (null == configCenterPort) {
  268. log.error("property[examcloud.startup.configCenterPort] is not specified");
  269. System.exit(-1);
  270. }
  271. }
  272. }