over 5 years ago
寫Spring還真的很少自爆就停止服務了,大概都是被 OS 層級終止(記憶體吃太多也是事實)
但線上服務還是加個服務監控怕真的掛了沒人知,台灣人用line比較多那再加個Line通知比較即時吧
Admin 監控端
依賴部分 就不用 Line 的 SDK 啦,因為它會攔截所有 request 但又沒處理好轉型問題,一般的 request 進來會報錯,反正 Http push message 到 Line 也很簡單
buildscript {
ext {
springBootVersion = '1.5.3.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
version = '0.0.1'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-mail')
compile 'de.codecentric:spring-boot-admin-server:1.5.0'
compile 'de.codecentric:spring-boot-admin-server-ui:1.5.0'
compile 'com.squareup.okhttp3:okhttp:3.8.0'
// compile 'com.linecorp.bot:line-bot-spring-boot:1.7.0'
compileOnly('org.projectlombok:lombok')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
這是 mail 配置跟 slack ,還有我們自己增加的 line 配置
server:
port: 9190
info:
version: 1.0.0
spring:
application:
name: 'ServiceAdmin'
mail:
host: "mail.xxx.com"
username: "ecs@xxx.com"
password: "xxxxxxxxxxx"
boot:
admin:
notify:
mail:
enabled: true
from: 'ec@xxxxxx.com'
to: 'sam@xxxxxx.com,chu@xxxxxx.com'
slack:
enabled: true
webhook-url: 'https://hooks.slack.com/services'
line:
enabled: true
channelSecret: 'xxxxxxxxxxxxxxxxx'
channelToken: 'xxxxxxxxxxxxxxxxx'
to: 'xxxxxxxxxxxxxxxxx'
讀取設定檔變數
import lombok.Data;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 自訂的設定檔
* Created by samchu on 2017/5/25.
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.boot.admin.notify.line")
@ConditionalOnProperty(prefix = "spring.boot.admin.notify.line", name = "enabled", matchIfMissing = true)
public class LineProperties {
private boolean enabled = false;
private String channelSecret;
private String channelToken;
private String to;
}
繼承 SpringBootAdmin 通知的 AbstractEventNotifier
import com.ps.admin.config.LineProperties;
import com.ps.admin.utils.LineHttp;
import de.codecentric.boot.admin.event.ClientApplicationEvent;
import de.codecentric.boot.admin.event.ClientApplicationRegisteredEvent;
import de.codecentric.boot.admin.event.ClientApplicationStatusChangedEvent;
import de.codecentric.boot.admin.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* 服務發生事件時會被通知
* 參考 https://github.com/codecentric/spring-boot-admin/blob/master/spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/notify/SlackNotifier.java
* 模板 http://www.jianshu.com/p/6a0a1fa453c8
* Created by samzh on 2017/4/6.
*/
@Slf4j
@Component
public class LineNotifier extends AbstractEventNotifier {
@Autowired
private LineProperties lineProperties;
private static final String DEFAULT_MESSAGE = "#{application.name} (#{application.id}) is #{to.status}";
private final SpelExpressionParser parser = new SpelExpressionParser();
private Expression message;
private List<String> notifyStatuses = Arrays.asList("UP", "DOWN", "OFFLINE");
@Autowired
private LineHttp lineHttp;
@Override
protected void doNotify(ClientApplicationEvent event) throws Exception {
if (lineProperties.isEnabled() == false) {
return;
}
this.message = parser.parseExpression(DEFAULT_MESSAGE, ParserContext.TEMPLATE_EXPRESSION);
if (event instanceof ClientApplicationRegisteredEvent) {
ClientApplicationRegisteredEvent registeredEvent = (ClientApplicationRegisteredEvent) event;
// System.out.println(registeredEvent.getApplication());// Application [id=2a87974b, name=boot-test, managementUrl=http://SAMPC:5566, healthUrl=http://SAMPC:5566/health, serviceUrl=http://SAMPC:5566]
// System.out.println(registeredEvent.getType());// REGISTRATION
// System.out.println(registeredEvent.getApplication().getServiceUrl());// http://SAMPC:5566
// System.out.println(registeredEvent.getApplication().getStatusInfo().getStatus());// UNKNOWN
}
if (event instanceof ClientApplicationStatusChangedEvent) {
ClientApplicationStatusChangedEvent statusChangedEvent = (ClientApplicationStatusChangedEvent) event;
String msg = message.getValue(event, String.class); // boot-test (2a87974b) is UP
lineHttp.post(msg);
}
}
}
發 http post 給 Line
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ps.admin.config.LineProperties;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Created by samchu on 2017/5/26.
* https://devdocs.line.me/en/?shell#push-message
*/
@Slf4j
@Component
public class LineHttp {
@Autowired
private LineProperties lineProperties;
public void post(String text) throws IOException {
if (lineProperties.isEnabled() == false) {
return;
}
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();
HashMap object = new HashMap<>();
object.put("to", lineProperties.getTo());
List messages = new ArrayList();
HashMap message = new HashMap<>();
message.put("type", "text");
message.put("text", text);
messages.add(message);
object.put("messages", messages);
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
ObjectMapper mapper = new ObjectMapper();
RequestBody body = RequestBody.create(JSON, mapper.writeValueAsString(object));
Request request = new Request.Builder()
.header("Authorization", String.format("Bearer %s", lineProperties.getChannelToken()))
.url("https://api.line.me/v2/bot/message/push")
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
log.warn("發送失敗" + response.body().toString());
//throw new IOException("Unexpected code " + response);
}
response.close();
}
}
}
Admin 被監控端
被監控端只需要添加此依賴
compile 'de.codecentric:spring-boot-admin-starter-client:1.5.0'
不果你如果有用 org.springframework.boot:spring-boot-starter-security 的話,預設 Actuator endpoints 是會被保護的,所以你被監控端必須自己告訴監控端我的 Auth 資料,監控端才能拿到更多資訊
security:
#此配置方式為 basicAuth
user:
name: admin
password: admin
spring:
boot:
admin:
url: 'http://localhost:9190'
client:
metadata:
user.name: ${security.user.name}
user.password: ${security.user.password}
這樣就可以了
如果你不用配置就可以正常使用,請注意你是否正在對外裸奔
結果