上一篇文章當中我們介紹了微服務在k8s環境下無感發布存在的問題和解決思路,今天來看一看程式碼的實現方式。
預熱以及快取載入
服務在容器啟動之後,可能需要載入一些必要快取,在這些快取載入之後,才能夠提供對外服務,註冊到註冊中心。在spring-cloud下,服務註冊是透過監聽WebServerInitializedEvent事件來觸發的,所以只需要保證在該事件之前載入完快取以及預熱完成即可。
預熱邏輯介面
首先,我們需要一個介面來定義預熱方法:
/**
* 預熱 邏輯介面
*/
public interface BaseWarmupHandler {
/**
* 預熱方法
*/
void warmup();
}
http預熱
接下來,我們實現一個預熱HTTP請求的方法
@RestController
@RequestMapping
public class WarmupController {
/**
* 系統預熱的介面
* 參考專案: <link url="https://github.com/steinsag/warm-me-up"/>
* @param request 請求引數
* @return 返回
*/
@PostMapping("/warmup")
public Response<String> post(@RequestBody @Valid WarmupRequest request) {
return Response.success(request.toString());
}
}
在這個控制器中,我們定義了一個/warmup的POST介面,用於觸發web容器相關預熱邏輯。
預熱邏輯實現
@Slf4j
@Service
public class ServletWarmupHandler implements BaseWarmupHandler{
@Resource
private Environment environment;
@Resource
private OkHttpClient okHttpClient;
@Override
public void warmup() {
final String serverPort = environment.getProperty("local.server.port");
final String baseUrl = "http://localhost:" + serverPort;
final String warmUpEndpoint = baseUrl + "/warmup";
log.debug("Sending REST request to warm up...");
// 建立 MediaType
MediaType mediaType = MediaType.get("application/json; charset=utf-8");
// 建立 RequestBody
RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(createSampleMessage()));
Request request = new Request.Builder()
.url(warmUpEndpoint)
.post(body)
.build();
try (Response response = okHttpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
log.error("warm up request error:{}", response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private WarmupRequest createSampleMessage() {
final WarmupRequest warmUpRequestDto = new WarmupRequest();
warmUpRequestDto.setInputMessage("warm me up");
warmUpRequestDto.setPatternString("paretern");
warmUpRequestDto.setSomeNumber(BigDecimal.TEN);
warmUpRequestDto.setEm(WarmupEnum.ME);
return warmUpRequestDto;
}
}
啟動時自動預熱
最後,我們需要在服務啟動時自動觸發預熱邏輯。這可以透過監聽ApplicationReadyEvent事件來實現:
@Service
public class WarmupService implements ApplicationListener<ApplicationReadyEvent> {
@Resource
private ApplicationContext applicationContext;
@Resource
private ApplicationEventPublisher eventPublisher;
@Override
public void onApplicationEvent(@NonNull ApplicationReadyEvent event) {
log.debug("warm up start .........");
Map<String, BaseWarmupHandler> warmupHandlerMap = applicationContext.getBeansOfType(BaseWarmupHandler.class);
for (Map.Entry<String, BaseWarmupHandler> entry : warmupHandlerMap.entrySet()) {
try {
entry.getValue().warmup();
} catch (Exception e) {
log.warn("warm up error, handler:{}, error:", entry.getKey(), e);
}
}
log.debug("warm up done!");
eventPublisher.publishEvent(new WarmupEndEvent(this));
}
}
這個事例僅僅提供了一個預熱web容器http請求的思路,在實際專案中要根據自身需求編寫@Valid,jdbc或者快取相關的預載入程式碼。
微服務就緒介面與Kubernetes就緒探針配置
在微服務架構中,每個服務的就緒時機可能各不相同,有的可能需要等待快取載入完成,有的則需要確保已在註冊中心成功註冊。為了準確反映服務的就緒狀態,專案應提供一個專門的就緒介面。以下是一個簡單的Java介面示例:
@RestController("/lifeCycle")
public class StartedController implements ApplicationListener<InstanceRegisteredEvent<?>> {
/** 專案是否啟動完成 */
private boolean started = false;
@GetMapping("/started")
public boolean started() {
if (started) {
return true;
}
// 這裡根據專案定義的異常型別返回
throw new RuntimeException("服務尚未啟動");
}
@Override
public void onApplicationEvent(InstanceRegisteredEvent<?> event) {
// InstanceRegisteredEvent 是spring-cloud 提供的註冊中心模版完成註冊時傳送的事件。
// 這個裡可以根據專案調整專案成功的時機
this.started = true;
}
}
在Kubernetes環境中,就緒探針(Readiness Probe)用於判斷服務是否已經準備好接收流量。以下是一個Kubernetes容器配置就緒探針的示例:
readinessProbe:
httpGet:
# host:連線使用的主機名,預設是 Pod 的 IP。也可以在 HTTP 頭中設定 "Host" 來代替。
# scheme:用於設定連線主機的方式(HTTP 還是 HTTPS)。預設是 "HTTP"。
# path:訪問 HTTP 服務的路徑。預設值為 "/"。
path: /lifeCycle/started
# httpHeaders:請求中自定義的 HTTP 頭。HTTP 頭欄位允許重複。
httpHeaders:
- name: Accept
value: application/json
# port:訪問容器的埠號或者埠名。如果數字必須在 1~65535 之間。
port: 10087
initialDelaySeconds: 5
# 執行探測的時間間隔(單位是秒)。預設是 10 秒。最小值是 1。 當容器未就緒時,ReadinessProbe 可能會在除配置的
periodSeconds: 5
提示:就緒探針透過HTTP請求訪問指定的路徑和埠,如果介面返回的狀態碼大於或等於200且小於400,則表示探測成功,服務已就緒;否則,表示探測失敗,服務尚未就緒。
引用
shelltea.warmup-spring-boot-starter
kubernetes文件