AppBootstrap.java 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 appSimpleName;
  42. private static String configCenterHost;
  43. private static String configCenterPort;
  44. /**
  45. * 启动方法
  46. *
  47. * @author WANGWEI
  48. * @param primarySource
  49. * @param args
  50. * @return
  51. */
  52. public static synchronized ConfigurableApplicationContext run(Class<?> primarySource,
  53. String... args) {
  54. ThreadContext.put("TRACE_ID", Thread.currentThread().getName());
  55. log.info("Starting...");
  56. checkBootstrapParams(args);
  57. sendStartupRequest2ConfigCenter();
  58. if (null != args) {
  59. for (String arg : args) {
  60. arg = arg.trim();
  61. if (arg.startsWith("--")) {
  62. String key = arg.substring(2, arg.indexOf("=")).trim();
  63. String value = arg.substring(arg.indexOf("=") + 1).trim();
  64. properties.put(key, value);
  65. }
  66. }
  67. }
  68. // 网卡选择
  69. String preferredNetworks = properties.get("examcloud.inet.preferredNetworks");
  70. if (StringUtils.isNotBlank(preferredNetworks)) {
  71. System.setProperty("spring.cloud.inetutils.preferred-networks", preferredNetworks);
  72. properties.put("spring.cloud.inetutils.preferred-networks", preferredNetworks);
  73. }
  74. properties.put("spring.profiles.active", active);
  75. Set<String> argSet = Sets.newLinkedHashSet();
  76. for (Entry<String, String> p : properties.entrySet()) {
  77. String key = p.getKey();
  78. String value = p.getValue();
  79. if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
  80. continue;
  81. }
  82. key = key.trim();
  83. value = value.trim();
  84. PropertyHolder.setProperty(p.getKey(), p.getValue());
  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("appSimpleName", appSimpleName);
  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("appSimpleName", appSimpleName);
  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 encryptedAppCode = resp.header("App-Code");
  172. String encryptedSecretKey = resp.header("Secret-Key");
  173. appId = StringUtil.toLong(BootstrapSecurityUtil.decrypt(encryptedAppId, startupCode));
  174. appCode = BootstrapSecurityUtil.decrypt(encryptedAppCode, startupCode);
  175. secretKey = BootstrapSecurityUtil.decrypt(encryptedSecretKey, startupCode);
  176. String decryptedBody = BootstrapSecurityUtil.decrypt(body, startupCode);
  177. @SuppressWarnings("unchecked")
  178. Map<String, String> map = JsonUtil.fromJson(decryptedBody, Map.class);
  179. properties = map;
  180. } catch (Exception e) {
  181. log.error("fail to send startup request to config center.", e);
  182. System.exit(-1);
  183. } finally {
  184. IOUtils.closeQuietly(resp);
  185. }
  186. }
  187. /**
  188. * 检查启动参数
  189. *
  190. * @author WANGWEI
  191. * @param args
  192. * @return
  193. */
  194. private static void checkBootstrapParams(String... args) {
  195. Properties props = new Properties();
  196. PropertiesUtil.loadFromResource("application.properties", props);
  197. if (null != args) {
  198. for (String s : args) {
  199. s = s.trim();
  200. if (s.startsWith("--spring.profiles.active=")) {
  201. active = s.substring(s.indexOf("=") + 1);
  202. } else if (s.startsWith("--examcloud.startup.startupCode=")) {
  203. startupCode = s.substring(s.indexOf("=") + 1);
  204. } else if (s.startsWith("--examcloud.startup.configCenterHost=")) {
  205. configCenterHost = s.substring(s.indexOf("=") + 1);
  206. } else if (s.startsWith("--examcloud.startup.configCenterPort=")) {
  207. configCenterPort = s.substring(s.indexOf("=") + 1);
  208. } else if (s.startsWith("--examcloud.startup.appSimpleName=")) {
  209. appSimpleName = s.substring(s.indexOf("=") + 1);
  210. }
  211. }
  212. }
  213. // active
  214. if (null == active) {
  215. String value = props.getProperty("spring.profiles.active");
  216. if (StringUtils.isNotBlank(value)) {
  217. active = value.trim();
  218. }
  219. }
  220. log.info("active=" + active);
  221. if (StringUtils.isBlank(active)) {
  222. log.error("property[spring.profiles.active] is not specified");
  223. System.exit(-1);
  224. }
  225. // startupCode
  226. if (null == startupCode) {
  227. String value = props.getProperty("examcloud.startup.startupCode");
  228. if (StringUtils.isNotBlank(value)) {
  229. startupCode = value.trim();
  230. }
  231. }
  232. log.info("startupCode=" + startupCode);
  233. if (StringUtils.isBlank(startupCode)) {
  234. log.error("property[examcloud.startup.startupCode] is not specified");
  235. System.exit(-1);
  236. }
  237. // appSimpleName
  238. if (null == appSimpleName) {
  239. String value = props.getProperty("examcloud.startup.appSimpleName");
  240. if (StringUtils.isNotBlank(value)) {
  241. appSimpleName = value.trim();
  242. }
  243. }
  244. log.info("appSimpleName=" + appSimpleName);
  245. if (StringUtils.isBlank(appSimpleName)) {
  246. log.error("property[examcloud.startup.appSimpleName] is not specified");
  247. System.exit(-1);
  248. }
  249. // configCenterHost
  250. if (null == configCenterHost) {
  251. String value = props.getProperty("examcloud.startup.configCenterHost");
  252. if (StringUtils.isNotBlank(value)) {
  253. configCenterHost = value.trim();
  254. }
  255. }
  256. log.info("configCenterHost=" + configCenterHost);
  257. if (StringUtils.isBlank(configCenterHost)) {
  258. log.error("property[examcloud.startup.configCenterHost] is not specified");
  259. System.exit(-1);
  260. }
  261. // configCenterPort
  262. if (null == configCenterPort) {
  263. String value = props.getProperty("examcloud.startup.configCenterPort");
  264. if (StringUtils.isNotBlank(value)) {
  265. configCenterPort = value.trim();
  266. }
  267. }
  268. log.info("configCenterPort=" + configCenterPort);
  269. if (null == configCenterPort) {
  270. log.error("property[examcloud.startup.configCenterPort] is not specified");
  271. System.exit(-1);
  272. }
  273. }
  274. }