ð�ð»完整列表已使用的内容:
Spring网络框架
Spring WebFlux反应性休息服务
gRPC Java Grpc
gRPC-Spring-Boot-Starter Grpc春季启动器
Salesforce Reactive gRPC Salesforce反应性GRPC
Spring Data R2DBC使用反应驱动程序
集成SQL数据库的规范
Zipkin开源,端到端分布的tracing
Spring Cloud Sleuth分布式跟踪的自动配置
Prometheus监视和警报
Grafana用于构成Prometheus
的所有内容的可观察性仪表板
Kubernetes自动部署,扩展和管理集装箱应用程序
Docker和Docker-Compose
Helm Kubernetes的包装经理
Flywaydb用于迁移
您可以在 GitHub repository 中找到源代码。
对于这个项目,让我们使用GRPC和PostgreSQL实施Spring Microservice。
以前有same one using Kotlin,
在LL上非常接近,但使用17 Java和Spring Webflux。
GRPC非常适合低潜伏期和高吞吐量通信,因此对于效率至关重要的微服务非常有用。
默认情况下,消息用Protobuf编码。虽然Protobuf有效地发送和接收二进制格式。
春天没有开箱即用的GRPC入门者,我们必须使用社区,最受欢迎的是 yidongnan
和 LogNet ,两者都很好并且可以使用,
对于这个项目,选择了第一个。
对于可反应性的GRPC可用Salesforce reactive-grpc。
在第一步,我们必须添加 gRPC Java Codegen Plugin for Protobuf Compiler 。
所有UI接口将在端口上可用:
Swagger UI:http://localhost:8000/webjars/swagger-ui/index.html
引导或Abaiaqian26
Zipkin UI:http://localhost:9411
Prometheus UI:http://localhost:9090
该项目的Docker-Compose文件:
version: "3.9"
services:
microservices_postgresql:
image: postgres:latest
container_name: microservices_postgresql
expose:
- "5432"
ports:
- "5432:5432"
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=bank_accounts
- POSTGRES_HOST=5432
command: -p 5432
volumes:
- ./docker_data/microservices_pgdata:/var/lib/postgresql/data
networks: [ "microservices" ]
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
command:
- --config.file=/etc/prometheus/prometheus.yml
volumes:
- ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro
networks: [ "microservices" ]
node_exporter:
container_name: microservices_node_exporter
restart: always
image: prom/node-exporter
ports:
- '9101:9100'
networks: [ "microservices" ]
grafana:
container_name: microservices_grafana
restart: always
image: grafana/grafana
ports:
- '3000:3000'
networks: [ "microservices" ]
zipkin:
image: openzipkin/zipkin:latest
restart: always
container_name: microservices_zipkin
ports:
- "9411:9411"
networks: [ "microservices" ]
networks:
microservices:
name: microservices
GRPC消息使用Protobuf序列化,Protobuf是一种有效的二进制消息格式,它在服务器和客户端上序列非常快,
及其序列化导致小消息有效载荷,在有限的带宽方案(例如移动应用程序)中很重要。
用于指定每个服务的RPC定义的接口合同将使用协议缓冲区定义。
每个微服务都将在此处为此定义一个原始文件。
首先,我们必须在proto文件中定义服务并对其进行编译,它最多具有一元方法和一台服务器流:
syntax = "proto3";
package com.example.grpc.bank.service;
import "google/protobuf/wrappers.proto";
import "google/protobuf/timestamp.proto";
service BankAccountService {
rpc createBankAccount (CreateBankAccountRequest) returns (CreateBankAccountResponse);
rpc getBankAccountById (GetBankAccountByIdRequest) returns (GetBankAccountByIdResponse);
rpc depositBalance (DepositBalanceRequest) returns (DepositBalanceResponse);
rpc withdrawBalance (WithdrawBalanceRequest) returns (WithdrawBalanceResponse);
rpc getAllByBalance (GetAllByBalanceRequest) returns (stream GetAllByBalanceResponse);
rpc getAllByBalanceWithPagination(GetAllByBalanceWithPaginationRequest) returns (GetAllByBalanceWithPaginationResponse);
}
message BankAccountData {
string id = 1;
string firstName = 2;
string lastName = 3;
string email = 4;
string address = 5;
string currency = 6;
string phone = 7;
double balance = 8;
string createdAt = 9;
string updatedAt = 10;
}
message CreateBankAccountRequest {
string email = 1;
string firstName = 2;
string lastName = 3;
string address = 4;
string currency = 5;
string phone = 6;
double balance = 7;
}
message CreateBankAccountResponse {
BankAccountData bankAccount = 1;
}
message GetBankAccountByIdRequest {
string id = 1;
}
message GetBankAccountByIdResponse {
BankAccountData bankAccount = 1;
}
message DepositBalanceRequest {
string id = 1;
double balance = 2;
}
message DepositBalanceResponse {
BankAccountData bankAccount = 1;
}
message WithdrawBalanceRequest {
string id = 1;
double balance = 2;
}
message WithdrawBalanceResponse {
BankAccountData bankAccount = 1;
}
message GetAllByBalanceRequest {
double min = 1;
double max = 2;
int32 page = 3;
int32 size = 4;
}
message GetAllByBalanceResponse {
BankAccountData bankAccount = 1;
}
message GetAllByBalanceWithPaginationRequest {
double min = 1;
double max = 2;
int32 page = 3;
int32 size = 4;
}
message GetAllByBalanceWithPaginationResponse {
repeated BankAccountData bankAccount = 1;
int32 page = 2;
int32 size = 3;
int32 totalElements = 4;
int32 totalPages = 5;
bool isFirst = 6;
bool isLast = 7;
}
GRPC的实际Maven依赖性:
<dependencies>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.13.1.RELEASE</version>
</dependency>
<dependency>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc-stub</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${java.grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${java.grpc.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.21.7</version>
</dependency>
</dependencies>
和Maven Protobuf插件:
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protobuf.protoc.version}:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${java.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protocPlugins>
<protocPlugin>
<id>reactor-grpc</id>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc</artifactId>
<version>1.2.3</version>
<mainClass>com.salesforce.reactorgrpc.ReactorGrpcGenerator</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
该插件为您的每个GRPC服务生成一个类。
例如: reactorBankCountServiceGrpc 其中 bankAccountgrpcService 是proto文件中GRPC服务的名称。
此类包含客户端存根和服务器 inflybase 您需要扩展。
汇编完成后,我们可以实施GRPC服务。
@grpcservice 允许我们通过特定于此服务的拦截器列表,因此我们可以在此处添加 loggrpcintector 。
为了验证请求,让我们使用 Spring-boot-starter-validation 使用 Hibernate Validator
@Slf4j
@GrpcService(interceptors = {LogGrpcInterceptor.class})
@RequiredArgsConstructor
public class BankAccountGrpcService extends ReactorBankAccountServiceGrpc.BankAccountServiceImplBase {
private final BankAccountService bankAccountService;
private final Tracer tracer;
private static final Long TIMEOUT_MILLIS = 5000L;
private final Validator validator;
@Override
@NewSpan
public Mono<CreateBankAccountResponse> createBankAccount(Mono<CreateBankAccountRequest> request) {
return request.flatMap(req -> bankAccountService.createBankAccount(validate(BankAccountMapper.of(req)))
.doOnNext(v -> spanTag("req", req.toString())))
.map(bankAccount -> CreateBankAccountResponse.newBuilder().setBankAccount(BankAccountMapper.toGrpc(bankAccount)).build())
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnSuccess(result -> log.info("created account: {}", result.getBankAccount()));
}
@Override
@NewSpan
public Mono<GetBankAccountByIdResponse> getBankAccountById(Mono<GetBankAccountByIdRequest> request) {
return request.flatMap(req -> bankAccountService.getBankAccountById(UUID.fromString(req.getId()))
.doOnNext(v -> spanTag("id", req.getId()))
.doOnSuccess(bankAccount -> spanTag("bankAccount", bankAccount.toString()))
.map(bankAccount -> GetBankAccountByIdResponse.newBuilder().setBankAccount(BankAccountMapper.toGrpc(bankAccount)).build()))
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnSuccess(response -> log.info("bankAccount: {}", response.getBankAccount()));
}
@Override
@NewSpan
public Mono<DepositBalanceResponse> depositBalance(Mono<DepositBalanceRequest> request) {
return request
.flatMap(req -> bankAccountService.depositAmount(UUID.fromString(req.getId()), BigDecimal.valueOf(req.getBalance()))
.doOnEach(v -> spanTag("req", req.toString()))
.map(bankAccount -> DepositBalanceResponse.newBuilder().setBankAccount(BankAccountMapper.toGrpc(bankAccount)).build()))
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnSuccess(response -> log.info("bankAccount: {}", response.getBankAccount()));
}
@Override
@NewSpan
public Mono<WithdrawBalanceResponse> withdrawBalance(Mono<WithdrawBalanceRequest> request) {
return request.flatMap(req -> bankAccountService.withdrawAmount(UUID.fromString(req.getId()), BigDecimal.valueOf(req.getBalance()))
.doOnNext(v -> spanTag("req", req.toString()))
.map(bankAccount -> WithdrawBalanceResponse.newBuilder().setBankAccount(BankAccountMapper.toGrpc(bankAccount)).build()))
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnSuccess(response -> log.info("bankAccount: {}", response.getBankAccount()));
}
@Override
@NewSpan
public Flux<GetAllByBalanceResponse> getAllByBalance(Mono<GetAllByBalanceRequest> request) {
return request
.flatMapMany(req -> bankAccountService.findBankAccountByBalanceBetween(BankAccountMapper.findByBalanceRequestDtoFromGrpc(req))
.doOnNext(v -> spanTag("req", req.toString()))
.map(bankAccount -> GetAllByBalanceResponse.newBuilder().setBankAccount(BankAccountMapper.toGrpc(bankAccount)).build()))
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnNext(response -> log.info("bankAccount: {}", response.getBankAccount()));
}
@Override
@NewSpan
public Mono<GetAllByBalanceWithPaginationResponse> getAllByBalanceWithPagination(Mono<GetAllByBalanceWithPaginationRequest> request) {
return request.flatMap(req -> bankAccountService.findAllBankAccountsByBalance(BankAccountMapper.findByBalanceRequestDtoFromGrpc(req))
.doOnNext(v -> spanTag("req", req.toString()))
.map(BankAccountMapper::toPaginationGrpcResponse))
.timeout(Duration.ofMillis(TIMEOUT_MILLIS))
.doOnError(this::spanError)
.doOnNext(response -> log.info("response: {}", response.toString()));
}
private <T> T validate(T data) {
var errors = validator.validate(data);
if (!errors.isEmpty()) throw new ConstraintViolationException(errors);
return data;
}
private void spanTag(String key, String value) {
var span = tracer.currentSpan();
if (span != null) span.tag(key, value);
}
private void spanError(Throwable ex) {
var span = tracer.currentSpan();
if (span != null) span.error(ex);
}
}
拦截器是一个GRPC概念,允许应用程序与传入或发出的GRPC调用交互。
他们提供了一种丰富请求处理管道的方法。
我们可以添加GRPC拦截器,在这里我们实现 logGrpCinterceptor :
@Slf4j
public class LogGrpcInterceptor implements ServerInterceptor {
@Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
log.info("service: {}, method: {}, headers: {}", call.getMethodDescriptor().getServiceName(), call.getMethodDescriptor().getBareMethodName(), headers.toString());
return next.startCall(call, headers);
}
}
并将其添加到全局 grpcglobalserverspector :
@Configuration(proxyBeanMethods = false)
public class GlobalInterceptorConfiguration {
@GrpcGlobalServerInterceptor
public LogGrpcInterceptor logServerInterceptor() {
return new LogGrpcInterceptor();
}
}
微服务的服务层有一些方法,例如,使用数据列表,它具有两种方法,
一个返回 pageimpl 用于一元方法响应的一个返回流量用于GRPC流响应方法。
当前的弹簧版本支持@transactional r2dbc
注释
界面和实现如下:
public interface BankAccountService {
Mono<BankAccount> createBankAccount(BankAccount bankAccount);
Mono<BankAccount> getBankAccountById(UUID id);
Mono<BankAccount> depositAmount(UUID id, BigDecimal amount);
Mono<BankAccount> withdrawAmount(UUID id, BigDecimal amount);
Flux<BankAccount> findBankAccountByBalanceBetween(FindByBalanceRequestDto request);
Mono<Page<BankAccount>> findAllBankAccountsByBalance(FindByBalanceRequestDto request);
}
@Slf4j
@Service
@RequiredArgsConstructor
public class BankAccountServiceImpl implements BankAccountService {
private final BankAccountRepository bankAccountRepository;
private final Tracer tracer;
@Override
@Transactional
@NewSpan
public Mono<BankAccount> createBankAccount(@SpanTag(key = "bankAccount") BankAccount bankAccount) {
return bankAccountRepository.save(bankAccount)
.doOnSuccess(savedBankAccount -> spanTag("savedBankAccount", savedBankAccount.toString()))
.doOnError(this::spanError);
}
@Override
@Transactional(readOnly = true)
@NewSpan
public Mono<BankAccount> getBankAccountById(@SpanTag(key = "id") UUID id) {
return bankAccountRepository.findById(id)
.doOnEach(v -> spanTag("id", id.toString()))
.switchIfEmpty(Mono.error(new BankAccountNotFoundException(id.toString())))
.doOnError(this::spanError);
}
@Override
@Transactional
@NewSpan
public Mono<BankAccount> depositAmount(@SpanTag(key = "id") UUID id, @SpanTag(key = "amount") BigDecimal amount) {
return bankAccountRepository.findById(id)
.switchIfEmpty(Mono.error(new BankAccountNotFoundException(id.toString())))
.flatMap(bankAccount -> bankAccountRepository.save(bankAccount.depositBalance(amount)))
.doOnError(this::spanError)
.doOnNext(bankAccount -> spanTag("bankAccount", bankAccount.toString()))
.doOnSuccess(bankAccount -> log.info("updated bank account: {}", bankAccount));
}
@Override
@Transactional
@NewSpan
public Mono<BankAccount> withdrawAmount(@SpanTag(key = "id") UUID id, @SpanTag(key = "amount") BigDecimal amount) {
return bankAccountRepository.findById(id)
.switchIfEmpty(Mono.error(new BankAccountNotFoundException(id.toString())))
.flatMap(bankAccount -> bankAccountRepository.save(bankAccount.withdrawBalance(amount)))
.doOnError(this::spanError)
.doOnNext(bankAccount -> spanTag("bankAccount", bankAccount.toString()))
.doOnSuccess(bankAccount -> log.info("updated bank account: {}", bankAccount));
}
@Override
@Transactional(readOnly = true)
@NewSpan
public Flux<BankAccount> findBankAccountByBalanceBetween(@SpanTag(key = "request") FindByBalanceRequestDto request) {
return bankAccountRepository.findBankAccountByBalanceBetween(request.min(), request.max(), request.pageable())
.doOnError(this::spanError);
}
@Override
@Transactional(readOnly = true)
@NewSpan
public Mono<Page<BankAccount>> findAllBankAccountsByBalance(@SpanTag(key = "request") FindByBalanceRequestDto request) {
return bankAccountRepository.findAllBankAccountsByBalance(request.min(), request.max(), request.pageable())
.doOnError(this::spanError)
.doOnSuccess(result -> log.info("result: {}", result.toString()));
}
private void spanTag(String key, String value) {
Optional.ofNullable(tracer.currentSpan()).ifPresent(span -> span.tag(key, value));
}
private void spanError(Throwable ex) {
Optional.ofNullable(tracer.currentSpan()).ifPresent(span -> span.error(ex));
}
}
R2DBC 是一种API,为关系数据库提供反应性的,非阻滞的API。
使用此功能,您可以在Spring Boot中将反应性API读取并以反应性/同步方式写入数据库。
bankrepository 是 reactivesortingRepository 的组合,我们的自定义 bankpostgresrepository 实现。
对于我们的自定义bankPostgresrepository在此处使用的实现 r2dbcentityTemplate 和 databaseclient 。
如果我们想像JPA provide一样具有类似的分页响应,
我们必须手动创建 pageimpl 。
public interface BankAccountRepository extends ReactiveSortingRepository<BankAccount, UUID>, BankAccountPostgresRepository {
Flux<BankAccount> findBankAccountByBalanceBetween(BigDecimal min, BigDecimal max, Pageable pageable);
}
public interface BankAccountPostgresRepository {
Mono<Page<BankAccount>> findAllBankAccountsByBalance(BigDecimal min, BigDecimal max, Pageable pageable);
}
@Slf4j
@Repository
@RequiredArgsConstructor
public class BankAccountPostgresRepositoryImpl implements BankAccountPostgresRepository {
private final DatabaseClient databaseClient;
private final R2dbcEntityTemplate template;
private final Tracer tracer;
@Override
@NewSpan
public Mono<Page<BankAccount>> findAllBankAccountsByBalance(@SpanTag(key = "min") BigDecimal min,
@SpanTag(key = "max") BigDecimal max,
@SpanTag(key = "pageable") Pageable pageable) {
var query = Query.query(Criteria.where(BALANCE).between(min, max)).with(pageable);
var listMono = template.select(query, BankAccount.class).collectList()
.doOnError(this::spanError)
.doOnSuccess(list -> spanTag("list", String.valueOf(list.size())));
var totalCountMono = databaseClient.sql("SELECT count(bank_account_id) as total FROM microservices.bank_accounts WHERE balance BETWEEN :min AND :max")
.bind("min", min)
.bind("max", max)
.fetch()
.one()
.doOnError(this::spanError)
.doOnSuccess(totalCount -> spanTag("totalCount", totalCount.toString()));
return Mono.zip(listMono, totalCountMono).map(tuple -> new PageImpl<>(tuple.getT1(), pageable, (Long) tuple.getT2().get("total")));
}
private void spanTag(String key, String value) {
Optional.ofNullable(tracer.currentSpan()).ifPresent(span -> span.tag(key, value));
}
private void spanError(Throwable ex) {
Optional.ofNullable(tracer.currentSpan()).ifPresent(span -> span.error(ex));
}
}
对于错误处理GRPC启动器的错误提供了 grpcadvice ,标志着要检查的类别的类别以获取异常处理方法,
@grpcexceptionhandler 标记要执行的注释方法,如果抛出了指定的例外,
状态代码很好地描述了here
@GrpcAdvice
@Slf4j
public class GrpcExceptionAdvice {
@GrpcExceptionHandler(RuntimeException.class)
public StatusException handleRuntimeException(RuntimeException ex) {
var status = Status.INTERNAL.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) RuntimeException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(BankAccountNotFoundException.class)
public StatusException handleBankAccountNotFoundException(BankAccountNotFoundException ex) {
var status = Status.NOT_FOUND.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) BankAccountNotFoundException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(InvalidAmountException.class)
public StatusException handleInvalidAmountException(InvalidAmountException ex) {
var status = Status.INVALID_ARGUMENT.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) InvalidAmountException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(DataAccessException.class)
public StatusException handleDataAccessException(DataAccessException ex) {
var status = Status.INVALID_ARGUMENT.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) DataAccessException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(ConstraintViolationException.class)
public StatusException handleConstraintViolationException(ConstraintViolationException ex) {
var status = Status.INVALID_ARGUMENT.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) ConstraintViolationException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(MethodArgumentNotValidException.class)
public StatusException handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
var status = Status.INVALID_ARGUMENT.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) MethodArgumentNotValidException: ", ex);
return status.asException();
}
@GrpcExceptionHandler(IllegalArgumentException.class)
public StatusException handleIllegalArgumentException(IllegalArgumentException ex) {
var status = Status.INVALID_ARGUMENT.withDescription(ex.getLocalizedMessage()).withCause(ex);
log.error("(GrpcExceptionAdvice) IllegalArgumentException: ", ex);
return status.asException();
}
}
与GRPC一起使用的UI客户量很少,个人喜欢使用 BloomRPC ,
另一个有用的工具是 grpcurl 和 grpcui 。
下一步让我们将我们的微服务部署到K8,
在此示例中,我们可以使用简单的多阶段文件来构建Docker映像:
FROM --platform=linux/arm64 azul/zulu-openjdk-alpine:17 as builder
ARG JAR_FILE=target/spring-webflux-grpc-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract
FROM azul/zulu-openjdk-alpine:17
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher", "-XX:MaxRAMPercentage=75", "-XX:+UseG1GC"]
对于使用K8的工作,喜欢使用 Helm ,微服务部署很简单,并且具有部署本身,服务,配置
和ServiceMonitor。
最后一个是因为监视使用 kube-prometheus-stack helm chart
微服务头盔图yaml文件是:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.microservice.name }}
labels:
app: {{ .Values.microservice.name }}
spec:
replicas: {{ .Values.microservice.replicas }}
template:
metadata:
name: {{ .Values.microservice.name }}
labels:
app: {{ .Values.microservice.name }}
spec:
containers:
- name: {{ .Values.microservice.name }}
image: {{ .Values.microservice.image }}
imagePullPolicy: Always
resources:
requests:
memory: {{ .Values.microservice.resources.requests.memory }}
cpu: {{ .Values.microservice.resources.requests.cpu }}
limits:
memory: {{ .Values.microservice.resources.limits.memory }}
cpu: {{ .Values.microservice.resources.limits.cpu }}
livenessProbe:
httpGet:
port: {{ .Values.microservice.livenessProbe.httpGet.port }}
path: {{ .Values.microservice.livenessProbe.httpGet.path }}
initialDelaySeconds: {{ .Values.microservice.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.microservice.livenessProbe.periodSeconds }}
readinessProbe:
httpGet:
port: {{ .Values.microservice.readinessProbe.httpGet.port }}
path: {{ .Values.microservice.readinessProbe.httpGet.path }}
initialDelaySeconds: {{ .Values.microservice.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.microservice.readinessProbe.periodSeconds }}
ports:
- containerPort: {{ .Values.microservice.ports.http.containerPort }}
name: {{ .Values.microservice.ports.http.name }}
- containerPort: {{ .Values.microservice.ports.grpc.containerPort}}
name: {{ .Values.microservice.ports.grpc.name }}
env:
- name: SPRING_APPLICATION_NAME
value: microservice_k8s
- name: JAVA_OPTS
value: "-XX:+UseG1GC -XX:MaxRAMPercentage=75"
- name: SERVER_PORT
valueFrom:
configMapKeyRef:
key: server_port
name: {{ .Values.microservice.name }}-config-map
- name: GRPC_SERVER_PORT
valueFrom:
configMapKeyRef:
key: grpc_server_port
name: {{ .Values.microservice.name }}-config-map
- name: SPRING_ZIPKIN_BASE_URL
valueFrom:
configMapKeyRef:
key: zipkin_base_url
name: {{ .Values.microservice.name }}-config-map
- name: SPRING_R2DBC_URL
valueFrom:
configMapKeyRef:
key: r2dbc_url
name: {{ .Values.microservice.name }}-config-map
- name: SPRING_FLYWAY_URL
valueFrom:
configMapKeyRef:
key: flyway_url
name: {{ .Values.microservice.name }}-config-map
restartPolicy: Always
terminationGracePeriodSeconds: {{ .Values.microservice.terminationGracePeriodSeconds }}
selector:
matchLabels:
app: {{ .Values.microservice.name }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.microservice.name }}-service
labels:
app: {{ .Values.microservice.name }}
spec:
selector:
app: {{ .Values.microservice.name }}
ports:
- port: {{ .Values.microservice.service.httpPort }}
name: http
protocol: TCP
targetPort: http
- port: {{ .Values.microservice.service.grpcPort }}
name: grpc
protocol: TCP
targetPort: grpc
type: ClusterIP
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
release: monitoring
name: {{ .Values.microservice.name }}-service-monitor
namespace: default
spec:
selector:
matchLabels:
app: {{ .Values.microservice.name }}
endpoints:
- interval: 10s
port: http
path: /actuator/prometheus
namespaceSelector:
matchNames:
- default
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.microservice.name }}-config-map
data:
server_port: "8080"
grpc_server_port: "8000"
zipkin_base_url: zipkin:9411
r2dbc_url: "r2dbc:postgresql://postgres:5432/bank_accounts"
flyway_url: "jdbc:postgresql://postgres:5432/bank_accounts"
和 values.yaml 文件:
microservice:
name: spring-webflux-grpc-microservice
image: alexanderbryksin/spring_webflux_grpc_microservice:latest
replicas: 1
livenessProbe:
httpGet:
port: 8080
path: /actuator/health/liveness
initialDelaySeconds: 60
periodSeconds: 5
readinessProbe:
httpGet:
port: 8080
path: /actuator/health/readiness
initialDelaySeconds: 60
periodSeconds: 5
ports:
http:
name: http
containerPort: 8080
grpc:
name: grpc
containerPort: 8000
terminationGracePeriodSeconds: 20
service:
httpPort: 8080
grpcPort: 8000
resources:
requests:
memory: '6000Mi'
cpu: "3000m"
limits:
memory: '6000Mi'
cpu: "3000m"
作为用于使用K8的UI工具,个人喜欢使用 Lens 。
您可以找到 GitHub repository here 的更多详细信息和源代码,
当然,总是在现实世界中,业务逻辑和基础架构代码要复杂得多,我们必须实施更多必要的功能。
我希望这篇文章有用和有益,并乐于收到任何反馈或问题,请随时 contact me email 或任何< strong> messengers :)