如何使用消息队列、Spring Boot和Kubernetes扩展微服务?
前端和后端紧密耦合——实际上它不能在没有后端的情况下处理应用
前端和后端必须一致扩展——如果没有足够的后端,你可能会淹没在流量中
如果后端不可用,则无法处理传入的事务。
如果后端不可用,则队列充当缓冲区
如果前端产生的消息多于后端可以处理的消息,则这些消息将缓冲在队列中
你可以独立于前端扩展后端——即你可以拥有数百个前端服务和后端的单个实例
一个购买物品的主页
管理面板,你可以在其中检查队列中的消息数
一个 /health 端点,用于在应用程序准备好接收流量时发出信号
一个 /submit 端点,从表单接收提交并在队列中创建消息
一个 /metrics 端点,用于公开队列中待处理消息的数量(稍后将详细介绍)
@Component
public class QueueService implements MessageListener {
private static final Logger LOGGER = LoggerFactory.getLogger(QueueService.class);
@Autowired
private JmsTemplate jmsTemplate;
public void send(String destination, String message) {
LOGGER.info("sending message='{}' to destination='{}'", message, destination);
jmsTemplate.convertAndSend(destination, message);
}
@Override
public void onMessage(Message message) {
if (message instanceof ActiveMQTextMessage) {
ActiveMQTextMessage textMessage = (ActiveMQTextMessage) message;
try {
LOGGER.info("Processing task " textMessage.getText());
Thread.sleep(5000);
LOGGER.info("Completed task " textMessage.getText());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (JMSException e) {
e.printStackTrace();
}
} else {
LOGGER.error("Message is not a text message " message.toString());
}
}
}
@SpringBootApplication
@EnableJms
public class SpringBootApplication implements JmsListenerConfigurer {
@Autowired
private QueueService queueService;
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
@Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("myId");
endpoint.setDestination("queueName");
endpoint.setMessageListener(queueService);
registrar.registerEndpoint(endpoint);
}
}
minikube start \
--memory 8096 \
--extra-config=controller-manager.horizontal-pod-autoscaler-upscale-delay=1m \
--extra-config=controller-manager.horizontal-pod-autoscaler-downscale-delay=2m \
--extra-config=controller-manager.horizontal-pod-autoscaler-sync-period=10s
minikube docker-env
docker build -t spring-k8s-hp0a .
docker images |grep spring
呈现前端的Spring Boot应用程序
ActiveMQ作为消息代理
处理事务的Spring Boot后端
Deployment对象,描述部署的容器及其配置
一个Service对象,充当Deployment部署创建的应用程序的所有实例的负载均衡器
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: queue
spec:
replicas: 1
template:
metadata:
labels:
app: queue
spec:
containers:
- name: web
image: webcenter/activemq:5.14.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 61616
resources:
limits:
memory: 512Mi
你从名为webcenter / activemq的官方仓库中请求了一个activemq容器
容器在端口61616上公开消息代理
为容器分配了512MB的内存
你要求提供单个副本 - 你的应用程序的单个实例
apiVersion: v1
kind: Service
metadata:
name: queue
spec:
ports:
- port: 61616
targetPort: 61616
selector:
app: queue
你创建了一个公开端口61616的负载均衡器
传入流量分发到所有具有app:queue类型标签的Pod(请参阅上面的部署)
targetPort是Pod暴露的端口
kubectl create -f activemq-deployment.yaml
kubectl create -f activemq-service.yaml
kubectl get pods -l=app=queue
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: spring-boot-hpa
imagePullPolicy: IfNotPresent
env:
- name: ACTIVEMQ_BROKER_URL
value: "tcp://queue:61616"
- name: STORE_ENABLED
value: "true"
- name: WORKER_ENABLED
value: "false"
ports:
- containerPort: 8080
livenessProbe:
initialDelaySeconds: 5
periodSeconds: 5
httpGet:
path: /health
port: 8080
resources:
limits:
memory: 512Mi
有一个section可以注入环境变量
还有Liveness探针,可以告诉你应用程序何时可以接受流量
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
ports:
- nodePort: 32000
port: 80
targetPort: 8080
selector:
app: frontend
type: NodePort
kubectl create -f fe-deployment.yaml
kubectl create -f fe-service.yaml
kubectl get pods -l=app=frontend
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: backend
spec:
replicas: 1
template:
metadata:
labels:
app: backend
annotations:
prometheus.io/scrape: 'true'
spec:
containers:
- name: backend
image: spring-boot-hpa
imagePullPolicy: IfNotPresent
env:
- name: ACTIVEMQ_BROKER_URL
value: "tcp://queue:61616"
- name: STORE_ENABLED
value: "false"
- name: WORKER_ENABLED
value: "true"
ports:
- containerPort: 8080
livenessProbe:
initialDelaySeconds: 5
periodSeconds: 5
httpGet:
path: /health
port: 8080
resources:
limits:
memory: 512Mi
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
ports:
- nodePort: 31000
port: 80
targetPort: 8080
selector:
app: backend
type: NodePort
kubectl create -f backend-deployment.yaml
kubectl create -f backend-service.yaml
kubectl get pods -l=app=backend
minikube service backend
minikube service frontend
你可以手动放大和缩小
你可以创建自动缩放规则以自动向上或向下扩展
kubectl scale --replicas=5 deployment/backend
kubectl get pods
kubectl scale --replicas=1 deployment/backend
# HELP messages Number of messages in the queue
# TYPE messages gauge
messages 0
cd spring-boot-k8s-hpa/monitoring
kubectl create -f ./metrics-server
kubectl create -f ./namespaces.yaml
kubectl create -f ./prometheus
kubectl create -f ./custom-metrics-api
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/messages" | jq .
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: spring-boot-hpa
spec:
scaleTargetRef:
apiVersion: extensions/v1beta1
kind: Deployment
name: backend
minReplicas: 1
maxReplicas: 10
metrics:
- type: Pods
pods:
metricName: messages
targetAverageValue: 10
Kubernetes监视scaleTargetRef中指定的部署。在这种情况下,它是工人。
你正在使用messages指标来扩展你的Pod。当队列中有超过十条消息时,Kubernetes将触发自动扩展。
至少,部署应该有两个Pod。10个Pod是上限。
kubectl create -f hpa.yaml
kubectl describe hpa
kubectl describe hpa
MAX(CURRENT_REPLICAS_LENGTH * 2, 4)
https://github.com/learnk8s/spring-boot-k8s-hpa
https://github.com/learnk8s/spring-boot-k8s-hpa/blob/master/src/main/java/com/learnk8s/app/queue/QueueService.java
https://kubernetes.io/docs/tasks/tools/
https://learnk8s.io/blog/installing-docker-and-kubernetes-on-windows
https://docs.docker.com/install/
https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler#deployment