Spring Boot

Spring Boot

一张图了解Spring Cloud微服务架构

老马 发表了文章 • 0 个评论 • 590 次浏览 • 2019-04-26 20:48 • 来自相关话题

Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置 ...查看全部
Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。Spring Cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A处获取服务。
6295401-8076ec880947ba79.png

Spring Cloud组成的微服务架构图

由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring Cloud中的组件分别充当其中的什么角色。

Fegin(接口调用):微服务之间通过Rest接口通讯,Spring Cloud提供Feign框架来支持Rest的调用,Feign使得不同进程的Rest接口调用得以用优雅的方式进行,这种优雅表现得就像同一个进程调用一样。

Netflix eureka(注册发现):微服务模式下,一个大的Web应用通常都被拆分为很多比较小的Web应用(服务),这个时候就需要有一个地方保存这些服务的相关信息,才能让各个小的应用彼此知道对方,这个时候就需要在注册中心进行注册。每个应用启动时向配置的注册中心注册自己的信息(IP地址,端口号, 服务名称等信息),注册中心将他们保存起来,服务间相互调用的时候,通过服务名称就可以到注册中心找到对应的服务信息,从而进行通讯。注册与发现服务为微服务之间的调用带来了方便,解决了硬编码的问题。服务间只通过对方的服务ID,而无需知道其IP和端口即可以获取对方方服务。

Ribbon(负载均衡):Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon,配置服务提供者的地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者的地址列表,并基于负载均衡算法,请求其中一个服务提供者的实例(为了服务的可靠性,一个微服务可能部署多个实例)。

Hystrix(熔断器):当服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃(雪崩效应)。Hystrix正是为了防止此类问题发生。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

* 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
* 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
* 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
* 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
* 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员指定。

Zuul(微服务网关):不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电影购票的手机APP,可能调用多个微服务的接口才能完成一次购票的业务流程,如果让客户端直接与各个微服务通信,会有以下的问题:

* 客户端会多次请求不同的微服务,增加了客户端的复杂性。
* 存在跨域请求,在一定场景下处理相对复杂。
* 认证复杂,每个服务都需要独立认证。
* 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将很难实施。
* 某些微服务可能使用了对防火墙/浏览器不友好的协议,直接访问时会有一定的困难。

以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。使用微服务网关后,微服务网关将封装应用程序的内部结构,客户端只用跟网关交互,而无须直接调用特定微服务的接口。这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

* 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
* 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
* 减少了客户端与各个微服务之间的交互次数。

Spring Cloud Bus( 统一配置服务):对于传统的单体应用,常使用配置文件管理所有配置。例如一个SpringBoot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。然而,在微服务架构中,微服务的配置管理一般有以下需求:

* 集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。
* 不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。
* 运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
* 配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。综上所述,对于微服务架构而言,一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。Spring Cloud Bus利用Git或SVN等管理配置、采用Kafka或者RabbitMQ等消息总线通知所有应用,从而实现配置的自动更新并且刷新所有微服务实例的配置。

Sleuth+ZipKin(跟踪服务):Sleuth和Zipkin结合使用可以通过图形化的界面查看微服务请求的延迟情况以及各个微服务的依赖情况。需要注意的是Spring Boot 2及以上不在支持Zipkin的自定义,需要到官方网站下载ZipKin相关的jar包。另外需要提一点的是Spring Boot Actuator,提供了很多监控端点如/actuator/info、/actuator/health、/acutator/refresh等,可以查看微服务的信息、健康状况、刷新配置等。

原文链接:一张图了解Spring Cloud微服务架构(作者:SimpleEasy)

如何使用消息队列,Spring Boot和Kubernetes扩展微服务

新牛哥 发表了文章 • 0 个评论 • 1457 次浏览 • 2019-02-10 14:10 • 来自相关话题

【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。 当你设计和构建大规模应用时, ...查看全部
【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。

当你设计和构建大规模应用时,你将面临两个重大挑战:可伸缩性和健壮性

你应该这样设计你的服务,即使它受到间歇性的重负载,它仍能可靠地运行。

以Apple Store为例。

每年都有数百万的Apple客户预先注册购买新的iPhone。

这是数百万人同时购买物品。

如果你要将Apple商店的流量描述为每秒的请求数量,那么它可能是下图的样子:
2.png

现在想象一下,你的任务是构建这样的应用程序。

你正在建立一个商店,用户可以在那里购买自己喜欢的商品。

你构建一个微服务来呈现网页并提供静态资产。 你还构建了一个后端REST API来处理传入的请求。

你希望将两个组件分开,因为这样可以使用相同的REST API,为网站和移动应用程序提供服务。
3.gif

今天是重要的一天,你的商店上线了。

你决定将应用程序扩展为前端四个实例和后端四个实例,因为你预测网站比平常更繁忙。
4.gif

你开始接收越来越多的流量。

前端服务正在很好得处理流量。 但是你注意到连接到数据库的后端正在努力跟上事务的数量。

不用担心,你可以将后端的副本数量扩展到8。
5.gif

你收到的流量更多,后端无法应对。

一些服务开始丢弃连接。 愤怒的客户与你的客服取得联系。 而现在你被淹没在大量流量中。

你的后端无法应付它,它会失去很多连接。
6.gif

你刚丢了一大笔钱,你的顾客也不高兴。

你的应用程序并没有设计得健壮且高可用:

  • 前端和后端紧密耦合——实际上它不能在没有后端的情况下处理应用
  • 前端和后端必须一致扩展——如果没有足够的后端,你可能会淹没在流量中
  • 如果后端不可用,则无法处理传入的事务。
失去事务意味着收入损失。你可以重新设计架构,以便将前端和后端用队列分离。
7.gif
前端将消息发布到队列,而后端则一次处理一个待处理消息。新架构有一些明显的好处:
  • 如果后端不可用,则队列充当缓冲区
  • 如果前端产生的消息多于后端可以处理的消息,则这些消息将缓冲在队列中
  • 你可以独立于前端扩展后端——即你可以拥有数百个前端服务和后端的单个实例
太好了,但是你如何构建这样的应用程序?你如何设计可处理数十万个请求的服务? 你如何部署动态扩展的应用程序?在深入了解部署和扩展的细节之前,让我们关注应用程序。#编写Spring应用程序该服务有三个组件:前端,后端和消息代理。前端是一个简单的Spring Boot Web应用程序,带有Thymeleaf模板引擎。后端是一个消耗队列消息的工作者。由于Spring Boot与JSM能出色得集成,因此你可以使用它来发送和接收异步消息。你可以在learnk8s / spring-boot-k8s-hpa中找到一个连接到JSM的前端和后端应用程序的示例项目。请注意,该应用程序是用Java 10编写的,以利用改进的Docker容器集成能力。只有一个代码库,你可以将项目配置为作为前端或后端运行。你应该知道该应用程序具有:
  • 一个购买物品的主页
  • 管理面板,你可以在其中检查队列中的消息数
  • 一个 `/health` 端点,用于在应用程序准备好接收流量时发出信号
  • 一个 `/submit` 端点,从表单接收提交并在队列中创建消息
  • 一个 `/metrics` 端点,用于公开队列中待处理消息的数量(稍后将详细介绍)
该应用程序可以在两种模式下运行:作为前端,应用程序呈现人们可以购买物品的网页。
8.png
作为工作者,应用程序等待队列中的消息并处理它们。
9.png
请注意,在示例项目中,使用`Thread.sleep(5000)`等待五秒钟来模拟处理。你可以通过更改`application.yaml`中的值来在任一模式下配置应用程序。#模拟应用程序的运行默认情况下,应用程序作为前端和工作程序启动。你可以运行该应用程序,只要你在本地运行ActiveMQ实例,你就应该能够购买物品并让系统处理这些物品。
10.gif
如果检查日志,则应该看到工作程序处理项目。它确实工作了!编写Spring Boot应用程序很容易。一个更有趣的主题是学习如何将Spring Boot连接到消息代理。#使用JMS发送和接收消息Spring JMS(Java消息服务)是一种使用标准协议发送和接收消息的强大机制。如果你以前使用过JDBC API,那么你应该熟悉JMS API,因为它的工作方式很类似。你可以按JMS方式来使用的最流行的消息代理是ActiveMQ——一个开源消息服务器。使用这两个组件,你可以使用熟悉的接口(JMS)将消息发布到队列(ActiveMQ),并使用相同的接口来接收消息。更妙的是,Spring Boot与JMS的集成非常好,因此你可以立即加快速度。实际上,以下短类封装了用于与队列交互的逻辑:
@Componentpublic 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());    }  }} 
你可以使用`send`方法将消息发布到命名队列。此外,Spring Boot将为每个传入消息执行`onMessage`方法。最后一个难题是指示Spring Boot使用该类。你可以通过在Spring Boot应用程序中注册侦听器来在后台处理消息,如下所示:
@SpringBootApplication@EnableJmspublic 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);  }} 
其中id是使用者的唯一标识符,destination是队列的名称。你可以从GitHub上的项目中完整地读取Spring队列服务的源代码。回顾一下你是如何在少于40行代码中编写可靠队列的。你一定很喜欢Spring Boot。# 你在部署时节省的所有时间都可以专注于编码你验证了应用程序的工作原理,现在是时候部署它了。此时,你可以启动VPS,安装Tomcat,并花些时间制作自定义脚本来测试,构建,打包和部署应用程序。或者你可以编写你希望拥有的描述:一个消息代理和两个使用负载均衡器部署的应用程序。诸如Kubernetes之类的编排器可以阅读你的愿望清单并提供正确的基础设施。由于花在基础架构上的时间减少意味着更多的时间编码,这次你将把应用程序部署到Kubernetes。但在开始之前,你需要一个Kubernetes集群。你可以注册Google云平台或Azure,并使用Kubernetes提供的云提供商服务。或者,你可以在将应用程序移动到云上之前在本地尝试Kubernetes。`minikube`是一个打包为虚拟机的本地Kubernetes集群。如果你使用的是Windows,Linux和Mac,那就太好了,因为创建群集需要五分钟。你还应该安装`kubectl`,即连接到你的群集的客户端。你可以从官方文档中找到有关如何安装`minikube`和`kubectl`的说明。

如果你在Windows上运行,则应查看有关如何安装Kubernetes和Docker的详细指南

你应该启动一个具有8GB RAM和一些额外配置的集群:
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`实例,则可以通过销毁VM来重新调整VM的大小。 只需添加`--memory 8096`就不会有任何影响。验证安装是否成功。 你应该看到以列表形式展示的一些资源。 集群已经准备就绪,也许你应该立即开始部署?还不行。你必须先装好你的东西。# 什么比uber-jar更好?容器部署到Kubernetes的应用程序必须打包为容器。毕竟,Kubernetes是一个容器编排器,所以它本身无法运行你的jar。容器类似于fat jar:它们包含运行应用程序所需的所有依赖项。甚至JVM也是容器的一部分。所以他们在技术上是一个更胖的fat-jar。将应用程序打包为容器的流行技术是Docker。虽然Docker是最受欢迎的,但它并不是唯一能够运行容器的技术。其他受欢迎的选项包括`rkt`和`lxd`。如果你没有安装Docker,可以按照Docker官方网站上的说明进行操作。通常,你构建容器并将它们推送到仓库。它类似于向Artifactory或Nexus推送jar包。但在这种特殊情况下,你将在本地工作并跳过仓库部分。实际上,你将直接在`minikube`中创建容器镜像。首先,按照此命令打印的说明将Docker客户端连接到`minikube`:
minikube docker-env
请注意,如果切换终端,则需要重新连接`minikube`内的Docker守护程序。 每次使用不同的终端时都应遵循相同的说明。并从项目的根目录构建容器镜像:
docker build -t spring-k8s-hp0a .
你可以验证镜像是否已构建并准备好运行:
docker images |grep spring 
很好。群集已准备好,你打包应用程序,也许你已准备好立即部署?是的,你最终可以要求Kubernetes部署应用程序。# 将你的应用程序部署到Kubernetes你的应用程序有三个组件:
  • 呈现前端的Spring Boot应用程序
  • ActiveMQ作为消息代理
  • 处理事务的Spring Boot后端
你应该分别部署这三个组件。对于每个组件你都应该创建:
  • Deployment对象,描述部署的容器及其配置
  • 一个Service对象,充当Deployment部署创建的应用程序的所有实例的负载均衡器
部署中的每个应用程序实例都称为Pod
11.gif
# 部署ActiveMQ让我们从ActiveMQ开始吧。你应该创建一个`activemq-deployment.yaml`文件,其中包含以下内容:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: queuespec:  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的内存
  • 你要求提供单个副本 - 你的应用程序的单个实例
使用以下内容创建`activemq-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: queuespec:  ports:  - port: 61616     targetPort: 61616  selector:    app: queue
幸运的是,这个模板更短!这个yaml表示:
  • 你创建了一个公开端口61616的负载均衡器
  • 传入流量分发到所有具有`app:queue`类型标签的Pod(请参阅上面的部署)
  • `targetPort`是Pod暴露的端口
你可以使用以下命令创建资源:
kubectl create -f activemq-deployment.yamlkubectl create -f activemq-service.yaml
你可以使用以下命令验证数据库的一个实例是否正在运行:
kubectl get pods -l=app=queue
# 部署前端使用以下内容创建`fe-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: frontendspec:  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
Deployment 看起来很像前一个。但是有一些新的字段:
  • 有一个section可以注入环境变量
  • 还有Liveness探针,可以告诉你应用程序何时可以接受流量
使用以下内容创建`fe-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: frontendspec:  ports:  - nodePort: 32000    port: 80    targetPort: 8080  selector:    app: frontend  type: NodePort
你可以使用以下命令创建资源:
kubectl create -f fe-deployment.yamlkubectl create -f fe-service.yaml
你可以使用以下命令验证前端应用程序的一个实例是否正在运行:
kubectl get pods -l=app=frontend
# 部署后端使用以下内容创建`backend-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: backendspec:  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
使用以下内容创建`backend-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: backend  spec:    ports:    - nodePort: 31000      port: 80      targetPort: 8080    selector:      app: backend    type: NodePort
你可以使用以下命令创建资源:
kubectl create -f backend-deployment.yamlkubectl create -f backend-service.yaml
你可以验证后端的一个实例是否正在运行:
kubectl get pods -l=app=backend
部署完成。它真的有效吗?你可以使用以下命令在浏览器中访问该应用程序:
minikube service backend
minikube service frontend
如果它有效,你应该尝试购买一些物品!工作者在处理交易吗?是的,如果有足够的时间,工作人员将处理所有待处理的消息。恭喜!你刚刚将应用程序部署到Kubernetes!# 手动扩展以满足不断增长的需求单个工作程序可能无法处理大量消息。 实际上,它当时只能处理一条消息。如果你决定购买数千件物品,则需要数小时才能清除队列。此时你有两个选择:
  • 你可以手动放大和缩小
  • 你可以创建自动缩放规则以自动向上或向下扩展
让我们先从基础知识开始。你可以使用以下方法将后端扩展为三个实例:
kubectl scale --replicas=5 deployment/backend
你可以验证Kubernetes是否创建了另外五个实例:
kubectl get pods
并且应用程序可以处理五倍以上的消息。一旦工人排空队列,你可以缩小:
kubectl scale --replicas=1 deployment/backend
如果你知道最多的流量何时达到你的服务,手动扩大和缩小都很棒。如果不这样做,设置自动缩放器允许应用程序自动缩放而无需手动干预。你只需要定义一些规则。# 公开应用程序指标Kubernetes如何知道何时扩展你的申请?很简单,你必须告诉它。自动调节器通过监控指标来工作。 只有这样,它才能增加或减少应用程序的实例。因此,你可以将队列长度公开为度量标准,并要求autoscaler观察该值。 队列中的待处理消息越多,Kubernetes将创建的应用程序实例就越多。那么你如何公开这些指标呢?应用程序具有 `/metrics` 端点以显示队列中的消息数。 如果你尝试访问该页面,你会注意到以下内容:
# HELP messages Number of messages in the queue # TYPE messages gaugemessages 0
应用程序不会将指标公开为JSON格式。 格式为纯文本,是公开Prometheus指标的标准。 不要担心记忆格式。 大多数情况下,你将使用其中一个Prometheus客户端库。# 在Kubernetes中使用应用程序指标你几乎已准备好进行自动缩放——但你应首先安装度量服务器。 实际上,默认情况下,Kubernetes不会从你的应用程序中提取指标。 如果你愿意,可以启用Custom Metrics API。要安装Custom Metrics API,你还需要Prometheus - 时间序列数据库。 安装Custom Metrics API所需的所有文件都可以方便地打包在learnk8s / spring-boot-k8s-hpa中。你应下载该存储库的内容,并将当前目录更改为该项目的`monitoring`文件夹。
cd spring-boot-k8s-hpa/monitoring
从那里,你可以创建自定义指标API:
kubectl create -f ./metrics-serverkubectl create -f ./namespaces.yamlkubectl create -f ./prometheuskubectl 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 .
恭喜,你有一个公开指标的应用程序和使用它们的指标服务器。你最终可以启用自动缩放器!# 在Kubernetes中进行自动扩展部署Kubernetes有一个名为Horizontal Pod Autoscaler的对象,用于监视部署并上下调整Pod的数量。你将需要其中一个来自动扩展实例。你应该创建一个包含以下内容的`hpa.yaml`文件:
apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata:  name: spring-boot-hpaspec:  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
自动定标器表示它能够将Pod扩展到2,并且它已准备好监视部署。令人兴奋的东西,但它有效吗?# 负载测试只有一种方法可以知道它是否有效:在队列中创建大量消息。转到前端应用程序并开始添加大量消息。 在添加消息时,使用以下方法监视Horizontal Pod Autoscaler的状态:
kubectl describe hpa
Pod的数量从2上升到4,然后是8,最后是10。该应用程序随消息数量而变化! 欢呼!你刚刚部署了一个完全可伸缩的应用程序,可根据队列中的待处理消息数进行扩展。另外,缩放算法如下:
MAX(CURRENT_REPLICAS_LENGTH * 2, 4)
在解释算法时,文档没有多大帮助。 你可以在代码中找到详细信息。此外,每分钟都会重新评估每个放大,而每两分钟缩小一次。以上所有都是可以调整的设置。但是你还没有完成。# 什么比自动缩放实例更好? 自动缩放群集。跨节点缩放Pod非常有效。 但是,如果群集中没有足够的容量来扩展Pod,该怎么办?如果达到峰值容量,Kubernetes将使Pods处于暂挂状态并等待更多资源可用。如果你可以使用类似于Horizontal Pod Autoscaler的自动缩放器,但对于节点则会很棒。好消息!你可以拥有一个群集自动缩放器,可以在你需要更多资源时为Kubernetes群集添加更多节点。
12.gif
群集自动缩放器具有不同的形状和大小。它也是特定于云提供商的。请注意,你将无法使用`minikube`测试自动缩放器,因为它根据定义是单节点。你可以在Github上找到有关集群自动调节器和云提供程序实现的更多信息。# 概览设计大规模应用程序需要仔细规划和测试。基于队列的体系结构是一种出色的设计模式,可以解耦你的微服务并确保它们可以独立扩展和部署。虽然你可以用脚本来扩展你的应用,但可以更轻松地利用容器编排器(如Kubernetes)自动部署和扩展应用程序。# 今天先说到这里感谢Nathan Cashmore和Andy Griffiths的反馈!如果你喜欢这篇文章,下面的文章可能你也会有兴趣:
  • 3个简单的技巧,用于较小的Docker镜像,并学习如何更快地构建和部署Docker镜像。
  • Kubernetes Chaos Engineering:经验教训 - 第1部分Kubernetes出现问题时会发生什么? Kubernetes可以从失败和自我愈合中恢复吗?
#成为Kubernetes中部署和扩展应用程序的专家学习如何使用Horizo​​ntal Pod Autoscaler部署和扩展应用程序只是一个开始!从我们的实践课程开始,学习如何掌握Kubernetes。了解如何:
  • 不费工夫得处理最繁忙的流量网站
  • 将你的工作扩展到数千台服务器,并将等待时间从几天缩短到几分钟
  • 知道你的应用程序具有多云设置的高可用性,让你高枕无忧
  • 只需使用你需要的资源,就可以节省大量现金
  • 为你的交付管道增压并全天候部署应用程序

成为Kubernetes的专家

原文链接:How to scale Microservices with Message Queues, Spring Boot, and Kubernetes (翻译:池剑锋)

Spring Boot Tomcat 容器化部署实践与总结

wise2c 发表了文章 • 0 个评论 • 1833 次浏览 • 2018-09-06 18:19 • 来自相关话题

在平时的工作和学习中经常会构建简单的web应用程序。如果只是HelloWorld级别的程序,使用传统的Spring+SpringMVC框架搭建得话会将大部分的时间花费在搭建框架本身上面,比如引入SpringMVC,配置DispatcheherServlet等。 ...查看全部
在平时的工作和学习中经常会构建简单的web应用程序。如果只是HelloWorld级别的程序,使用传统的Spring+SpringMVC框架搭建得话会将大部分的时间花费在搭建框架本身上面,比如引入SpringMVC,配置DispatcheherServlet等。并且这些配置文件都差不多,重复这些劳动似乎意义不大。所以使用Springboot框架来搭建简单的应用程序显得十分的便捷和高效。
微信图片_20180906110558.png


前两天在工作中需要一个用于测试文件下载的简单web程序,条件是使用Tomcat Docker Image作为载体,所以为了方便就使用了SpringBoot框架快速搭建起来。

程序写出来在本机能够正常的跑起来,准备制作镜像,但是闻题就接踵而来了。首先是部署的问题,SpringBoot Web程序默认打的是jar包,运行时使用命令 java -jar -Xms128m -Xmx128m xxx.jar,本机跑的没问题。但是需求是使用外部的tomcat容器而不是tomcat-embed,所以查阅官方文档如下:

The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. Doing so makes use of Spring Framework’s Servlet 3.0 support and lets you configure your application when it is launched by the servlet container. Typically, you should update your application’s main class to extend SpringBootServletInitializer, as shown in the following example:
 @SpringBootApplication
public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}

}

The next step is to update your build configuration such that your project produces a war file rather than a jar file. If you use Maven and spring-boot-starter-parent(which configures Maven’s war plugin for you), all you need to do is to modify pom.xml to change the packaging to war, as follows:
war

If you use Gradle, you need to modify build.gradle to apply the war plugin to the project, as follows:
apply plugin: 'war'

The final step in the process is to ensure that the embedded servlet container does not interfere with the servlet container to which the war file is deployed. To do so, you need to mark the embedded servlet container dependency as being provided.

If you use Maven, the following example marks the servlet container (Tomcat, in this case) as being provided:
 


org.springframework.boot
spring-boot-starter-tomcat
provided




If you use Gradle, the following example marks the servlet container (Tomcat, in this case) as being provided:
 dependencies {
// …
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// …
}

综上所述,将SpringBoot程序放入Tomcat运行有两步。第一,SpringBoot启动类继承SpringBootServletInitializer,重写configure方法。第二,将包管理软件的打包方式改成war,并将Spring-boot-starter-tomcat设置为provided。但是,为什么应该这么做?

根据Servlet3.0规范可知,Web容器启动时通过ServletContainerInitializer类实现第三方组件的初始化工作,如注册servlet或filter等,每个框架要是用ServletContainerInitializer就必须在对应的META-INF/services目录下创建名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,在SpringMVC框架中为SpringServletContainerInitializer。一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。如下为SpringServletContainerInitializer源代码:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext
servletContext)throws ServletException {
List initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers())
&&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
// 将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型
的类都传入到onStartup方法的Set>;为这些
WebApplicationInitializer类型的类创建实例。
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass)
.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate
WebApplicationInitializer class", ex);
}
}
}
}

if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected
on classpath");
return;
}

servletContext.log(initializers.size() + " Spring WebApplicationInitializers
detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
//为每个WebApplicationInitializer调用自己的onStartup()
initializer.onStartup(servletContext);
}
}

}

SpringBootInitializer继承WebApplicationInitializer,重写的onStartup如下:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(getClass());
// 调用自生createRootApplicationContext()方法
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 调用重写方法,重写方法传入SpringBoot启动类
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
//启动应用程序,就是启动传入的SpringBoot程序
return run(application);
}

在程序和Tomcat打通之后需做的就是将war打成一个Docker镜像,如果每次都是复制war包,然后再docker build会很麻烦,在开源社区早有了解决方案–docker-maven-plugin,查看Github中的使用方法,将如下内容加入pom.xml中:

com.spotify
docker-maven-plugin
1.1.1

wanlinus/file-server




${project.basedir}



/
${project.build.directory}
${project.build.finalName}.war





该配置中有个标签是用来指定构建docker image的Dockerfile的位置,在项目的根目录下新建一个Dockerfile,内容如下:

FROM tomcat
MAINTAINER wanlinus
WORKDIR /docker
COPY target/file-server-0.0.1-SNAPSHOT.war ./server.war
RUN mkdir $CATALINA_HOME/webapps/server \
&& mv /docker/server.war $CATALINA_HOME/webapps/server \
&& unzip $CATALINA_HOME/webapps/server/server.war -d $CATALINA_HOME/webapps/server/ \
&& rm $CATALINA_HOME/webapps/server/server.war \
&& cd $CATALINA_HOME/webapps/server && echo "asd" > a.txt
EXPOSE 8080


终端中输入
mvn clean package docker:build


在本地将会生成一个docker image,如果docker没有运行于本地,需要在标签中输入远端地址和docker daemon端口。

最后在终端中运行
docker run --rm -p 8080:8080 wanlinus/fileserver


在Tomcat启动后将会看到Spring Boot程序的启动日志,至此,Spring Boot Tomcat容器化完成。

原文链接:https://mp.weixin.qq.com/s/Vb4VzO7AT9PZ6QPtCe4SLw

分布式实时日志分析解决方案 ELK 部署架构

Java高级开发 发表了文章 • 0 个评论 • 1558 次浏览 • 2018-03-25 17:19 • 来自相关话题

一、概述 ELK 已经成为目前最流行的集中式日志解决方案,它主要是由Beats、Logstash、Elasticsearch、Kibana等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案。本文将会介绍ELK常见的架 ...查看全部
一、概述

ELK 已经成为目前最流行的集中式日志解决方案,它主要是由Beats、Logstash、Elasticsearch、Kibana等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案。本文将会介绍ELK常见的架构以及相关问题解决。

Filebeat:Filebeat是一款轻量级,占用服务资源非常少的数据收集引擎,它是ELK家族的新成员,可以代替Logstash作为在应用服务器端的日志收集引擎,支持将收集到的数据输出到Kafka,Redis等队列。 Logstash:数据收集引擎,相较于Filebeat比较重量级,但它集成了大量的插件,支持丰富的数据源收集,对收集的数据可以过滤,分析,格式化日志格式。 Elasticsearch:分布式数据搜索引擎,基于Apache Lucene实现,可集群,提供数据的集中式存储,分析,以及强大的数据搜索和聚合功能。 Kibana:数据的可视化平台,通过该web平台可以实时的查看 Elasticsearch 中的相关数据,并提供了丰富的图表统计功能。

二、ELK常见部署架构

2.1、Logstash作为日志收集器

这种架构是比较原始的部署架构,在各应用服务器端分别部署一个Logstash组件,作为日志收集器,然后将Logstash收集到的数据过滤、分析、格式化处理后发送至Elasticsearch存储,最后使用Kibana进行可视化展示,这种架构不足的是:Logstash比较耗服务器资源,所以会增加应用服务器端的负载压力。

2.2、Filebeat作为日志收集器

该架构与第一种架构唯一不同的是:应用端日志收集器换成了Filebeat,Filebeat轻量,占用服务器资源少,所以使用Filebeat作为应用服务器端的日志收集器,一般Filebeat会配合Logstash一起使用,这种部署方式也是目前最常用的架构。

2.3、引入缓存队列的部署架构

该架构在第二种架构的基础上引入了Kafka消息队列(还可以是其他消息队列),将Filebeat收集到的数据发送至Kafka,然后在通过Logstasth读取Kafka中的数据,这种架构主要是解决大数据量下的日志收集方案,使用缓存队列主要是解决数据安全与均衡Logstash与Elasticsearch负载压力。

2.4、以上三种架构的总结

第一种部署架构由于资源占用问题,现已很少使用,目前使用最多的是第二种部署架构,至于第三种部署架构个人觉得没有必要引入消息队列,除非有其他需求,因为在数据量较大的情况下,Filebeat 使用压力敏感协议向 Logstash 或 Elasticsearch 发送数据。如果 Logstash 正在繁忙地处理数据,它会告知 Filebeat 减慢读取速度。拥塞解决后,Filebeat 将恢复初始速度并继续发送数据。

三、问题及解决方案

问题:如何实现日志的多行合并功能?

系统应用中的日志一般都是以特定格式进行打印的,属于同一条日志的数据可能分多行进行打印,那么在使用ELK收集日志的时候就需要将属于同一条日志的多行数据进行合并。

解决方案:使用Filebeat或Logstash中的multiline多行合并插件来实现

在使用multiline多行合并插件的时候需要注意,不同的ELK部署架构可能multiline的使用方式也不同,如果是本文的第一种部署架构,那么multiline需要在Logstash中配置使用,如果是第二种部署架构,那么multiline需要在Filebeat中配置使用,无需再在Logstash中配置multiline。

1、multiline在Filebeat中的配置方式:

filebeat.prospectors:

  • paths:
  • /home/project/elk/logs/test.log
input_type: logmultiline:pattern: '^['negate: truematch: afteroutput: logstash:hosts: ["localhost:5044"]pattern:正则表达式 negate:默认为false,表示匹配pattern的行合并到上一行;true表示不匹配pattern的行合并到上一行 match:after表示合并到上一行的末尾,before表示合并到上一行的行首 如:pattern: '['negate: truematch: after该配置表示将不匹配pattern模式的行合并到上一行的末尾2、multiline在Logstash中的配置方式input {beats {port => 5044}}filter {multiline {pattern => "%{LOGLEVEL}s*]"negate => truewhat => "previous"}}output {elasticsearch {hosts => "localhost:9200"}}(1)Logstash中配置的what属性值为previous,相当于Filebeat中的after,Logstash中配置的what属性值为next,相当于Filebeat中的before。(2)pattern => “%{LOGLEVEL}s*]” 中的LOGLEVEL是Logstash预制的正则匹配模式,预制的还有好多常用的正则匹配模式,详细请看:https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns问题:如何将Kibana中显示日志的时间字段替换为日志信息中的时间?默认情况下,我们在Kibana中查看的时间字段与日志信息中的时间不一致,因为默认的时间字段值是日志收集时的当前时间,所以需要将该字段的时间替换为日志信息中的时间。解决方案:使用grok分词插件与date时间格式化插件来实现在Logstash的配置文件的过滤器中配置grok分词插件与date时间格式化插件,如:input {beats {port => 5044}}filter {multiline {pattern => "%{LOGLEVEL}s*][%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME}]" negate => true what => "previous"}grok {match => [ "message" , "(?%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME})" ]}date {match => ["customer_time", "yyyyMMdd HH:mm:ss,SSS"] //格式化时间target => "@timestamp"//替换默认的时间字段}}output { elasticsearch{hosts => "localhost:9200"}}如要匹配的日志格式为:“[DEBUG][20170811 10:07:31,359][DefaultBeanDefinitionDocumentReader:106] Loading bean definitions”,解析出该日志的时间字段的方式有:① 通过引入写好的表达式文件,如表达式文件为customer_patterns,内容为: CUSTOMER_TIME %{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME}注:内容格式为:[自定义表达式名称] [正则表达式] 然后logstash中就可以这样引用:filter {grok {patterns_dir => ["./customer-patterms/mypatterns"] //引用表达式文件路径match => [ "message" , "%{CUSTOMER_TIME:customer_time}" ] //使用自定义的grok表达式 }}123456② 以配置项的方式,规则为:(?<自定义表达式名称>正则匹配规则),如:filter {grok {match => [ "message" , "(?%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME})" ] }}12345问题:如何在Kibana中通过选择不同的系统日志模块来查看数据一般在Kibana中显示的日志数据混合了来自不同系统模块的数据,那么如何来选择或者过滤只查看指定的系统模块的日志数据?解决方案:新增标识不同系统模块的字段或根据不同系统模块建ES索引1、新增标识不同系统模块的字段,然后在Kibana中可以根据该字段来过滤查询不同模块的数据 这里以第二种部署架构讲解,在Filebeat中的配置内容为:filebeat.prospectors:-paths:
  • /home/project/elk/logs/account.log
input_type: logmultiline:pattern: '^['negate: truematch: afterfields: //新增log_from字段log_from: account-paths:
  • /home/project/elk/logs/customer.log
input_type: logmultiline:pattern: '^['negate: truematch: afterfields:log_from: customeroutput:logstash:hosts: ["localhost:5044"]通过新增:log_from字段来标识不同的系统模块日志2、根据不同的系统模块配置对应的ES索引,然后在Kibana中创建对应的索引模式匹配,即可在页面通过索引模式下拉框选择不同的系统模块数据。 这里以第二种部署架构讲解,分为两步: ① 在Filebeat中的配置内容为:filebeat.prospectors:-paths:
  • /home/project/elk/logs/account.log
input_type: logmultiline:pattern: '^['negate: truematch: afterdocument_type: account-paths:
  • /home/project/elk/logs/customer.log

input_type: log

multiline:

pattern: '^['

negate: truematch: after

document_type: customeroutput: logstash:

hosts: ["localhost:5044"]

通过document_type来标识不同系统模块

② 修改Logstash中output的配置内容为:

output {

elasticsearch {

hosts => "localhost:9200"

index => "%{type}"

} }123

在output中增加index属性,%{type}表示按不同的document_type值建ES索引

四、总结

本文主要介绍了ELK实时日志分析的三种部署架构,以及不同架构所能解决的问题,这三种架构中第二种部署方式是时下最流行也是最常用的部署方式,最后介绍了ELK作在日志分析中的一些问题与解决方案,说在最后,ELK不仅仅可以用来作为分布式日志数据集中式查询和管理,还可以用来作为项目应用以及服务器资源监控等场景。

使用Spring Cloud和Docker构建微服务架构

李颖杰 发表了文章 • 0 个评论 • 20841 次浏览 • 2017-10-07 21:49 • 来自相关话题

【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式 ...查看全部
【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式的起点。

该代码可以在GitHub上获得,并且在Docker Hub上提供了镜像。您只需要一个命令即可启动整个系统。

我选择了一个老项目作为这个系统的基础,它的后端以前是单一应用。此应用提供了处理个人财务、整理收入开销、管理储蓄、分析统计和创建简单预测等功能。
#功能服务
整个应用分解为三个核心微服务。它们都是可以独立部署的应用,围绕着某些业务功能进行组织。
1.png


账户服务

包含一般用户输入逻辑和验证:收入/开销记录、储蓄和账户设置。
biao1.png


统计服务

计算主要的统计参数,并捕获每一个账户的时间序列。数据点包含基于货币和时间段正常化后的值。该数据可用于跟踪账户生命周期中的现金流量动态。
biao2.png


通知服务

存储用户的联系信息和通知设置(如提醒和备份频率)。安排工作人员从其它服务收集所需的信息并向订阅的客户发送电子邮件。
biao3.png


注意

* 每一个微服务拥有自己的数据库,因此没有办法绕过API直接访问持久数据。
* 在这个项目中,我使用MongoDB作为每一个服务的主数据库。拥有一个多种类持久化架构(polyglot persistence architecture)也是很有意义的。
* 服务间(Service-to-service)通信是非常简单的:微服务仅使用同步的REST API进行通信。现实中的系统的常见做法是使用互动风格的组合。例如,执行同步的GET请求检索数据,并通过消息代理(broker)使用异步方法执行创建/更新操作,以便解除服务和缓冲消息之间的耦合。然而,这带给我们是最终的一致性

#基础设施服务
分布式系统中常见的模式,可以帮助我们描述核心服务是怎样工作的。Spring Cloud提供了强大的工具,可以增强Spring Boot应用的行为来实现这些模式。我会简要介绍一下:
2.png

##配置服务
Spring Cloud Config是分布式系统的水平扩展集中式配置服务。它使用了当前支持的本地存储、Git和Subversion等可拔插存储库层(repository layer)。

在此项目中,我使用了native profile,它简单地从本地classpath下加载配置文件。您可以在配置服务资源中查看shared目录。现在,当通知服务请求它的配置时,配置服务将响应回shared/notification-service.yml和shared/application.yml(所有客户端应用之间共享)。

客户端使用

只需要使用sprng-cloud-starter-config依赖构建Spring Boot应用,自动配置将会完成其它工作。

现在您的应用中不需要任何嵌入的properties,只需要提供有应用名称和配置服务url的bootstrap.yml即可:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true

使用Spring Cloud Config,您可以动态更改应用配置

比如,EmailService bean使用了@RefreshScope注解。这意味着您可以更改电子邮件的内容和主题,而无需重新构建和重启通知服务应用。

首先,在配置服务器中更改必要的属性。然后,对通知服务执行刷新请求:curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh。

您也可以使用webhook来自动执行此过程

注意

* 动态刷新存在一些限制。@RefreshScope不能和@Configuraion类一同工作,并且不会作用于@Scheduled方法。
* fail-fast属性意味着如果Spring Boot应用无法连接到配置服务,将会立即启动失败。当您一起启动所有应用时,这非常有用。
* 下面有重要的安全提示

##授权服务
负责授权的部分被完全提取到单独的服务器,它为后端资源服务提供OAuth2令牌。授权服务器用于用户授权以及在周边内进行安全的机器间通信。

在此项目中,我使用密码凭据作为用户授权的授权类型(因为它仅由本地应用UI使用)和客户端凭据作为微服务授权的授权类型。

Spring Cloud Security提供了方便的注解和自动配置,使其在服务器端或者客户端都可以很容易地实现。您可以在文档中了解到更多信息,并在授权服务器代码中检查配置明细。

从客户端来看,一切都与传统的基于会话的授权完全相同。您可以从请求中检索Principal对象、检查用户角色和其它基于表达式访问控制和@PreAuthorize注解的内容。

PiggyMetrics(帐户服务、统计服务、通知服务和浏览器)中的每一个客户端都有一个范围:用于后台服务的服务器、用于浏览器展示的UI。所以我们也可以保护控制器避免受到外部访问,例如:
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}

##API网关
您可以看到,有三个核心服务。它们向客户端暴露外部API。在现实系统中,这个数量可以非常快速地增长,同时整个系统将变得非常复杂。实际上,一个复杂页面的渲染可能涉及到数百个服务。

理论上,客户端可以直接向每个微服务直接发送请求。但是这种方式是存在挑战和限制的,如果需要知道所有端点的地址,分别对每一段信息执行http请求,将结果合并到客户端。另一个问题是,这不是web友好协议,可能只在后端使用。

通常一个更好的方法是使用API网关。它是系统的单个入口点,用于通过将请求路由到适当的后端服务或者通过调用多个后端服务并聚合结果来处理请求。此外,它还可以用于认证、insights、压力测试、金丝雀测试(canary testing)、服务迁移、静态响应处理和主动变换管理。

Netflix开源这样的边缘服务,现在用Spring Cloud,我们可以用一个@EnabledZuulProxy注解来启用它。在这个项目中,我使用Zuul存储静态内容(UI应用),并将请求路由到适当的微服务。以下是一个简单的基于前缀(prefix-based)路由的通知服务配置:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false

这意味着所有以/notification开头的请求将被路由到通知服务。您可以看到,里面没有硬编码的地址。Zuul使用服务发现机制来定位通知服务实例以及断路器和负载均衡器,如下所述。
##服务发现
另一种常见的架构模式是服务发现。它允许自动检测服务实例的网络位置,由于自动扩展、故障和升级,它可能会动态分配地址。

服务发现的关键部分是注册。我使用Netflix Eureka进行这个项目,当客户端需要负责确定可以用的服务实例(使用注册服务器)的位置和跨平台的负载均衡请求时,Eureka就是客户端发现模式的一个很好的例子。

使用Spring Boot,您可以使用spring-cloud-starter-eureka-server依赖、@EnabledEurekaServer注解和简单的配置属性轻松构建Eureka注册中心(Eureka Registry)。

使用@EnabledDiscoveryClient注解和带有应用名称的bootstrap.yml来启用客户端支持:
spring:
application:
name: notification-service

现在,在应用启动时,它将向Eureka服务器注册并提供元数据,如主机和端口、健康指示器URL、主页等。Eureka接收来自从属于某服务的每个实例的心跳消息。如果心跳失败超过配置的时间表,该实例将从注册表中删除。

此外,Eureka还提供了一个简单的界面,您可以通过它来跟踪运行中的服务和可用实例的数量:http://localhost:8761
3.png

##负载均衡器、断路器和Http客户端
Netflix OSS提供了另一套很棒的工具。

Ribbon

Ribbon是一个客户端负载均衡器,可以很好地控制HTTP和TCP客户端的行为。与传统的负载均衡器相比,每次线上调用都不需要额外的跳跃——您可以直接联系所需的服务。

它与Spring Cloud和服务发现是集成在一起的,可开箱即用。Eureka客户端提供了可用服务器的动态列表,因此Ribbon可以在它们之间进行平衡。

Hystrix

Hystrix是断路器模式的一种实现,它可以通过网络访问依赖来控制延迟和故障。中心思想是在具有大量微服务的分布式环境中停止级联故障。这有助于快速失败并尽快恢复——自我修复在容错系统中是非常重要的。

除了断路器控制,在使用Hystrix,您可以添加一个备用方法,在主命令失败的情况下,该方法将被调用以获取默认值。

此外,Hystrix生成每个命令的执行结果和延迟的度量,我们可以用它来监视系统的行为

Feign

Feign是一个声明式HTTP客户端,能与Ribbon和Hystrix无缝集成。实际上,通过一个spring-cloud-starter-feign依赖和@EnabledFeignClients注解,您可以使用一整套负载均衡器、断路器和HTTP客户端,并附带一个合理的的默认配置。

以下是账户服务的示例:
@FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}


* 您需要的只是一个接口
* 您可以在Spring MVC控制器和Feign方法之间共享@RequestMapping部分
* 以上示例仅指定所需要的服务ID——statistics-service,这得益于Eureka的自动发现(但显然您可以使用特定的URL访问任何资源)。

##监控仪表盘
在这个项目配置中,Hystrix的每一个微服务都通过Spring Cloud Bus(通过AMQP broker)将指标推送到Turbine。监控项目只是一个使用了TurbineHystrix仪表盘的小型Spring Boot应用。

让我们看看系统行为在负载下:账户服务调用统计服务和它在一个变化的模拟延迟下的响应。响应超时阈值设置为1秒。
4.png

##日志分析
集中式日志记录在尝试查找分布式环境中的问题时非常有用。Elasticsearch、Logstash和Kibana技术栈可让您轻松搜索和分析您的日志、利用率和网络活动数据。在我的另一个项目中已经有现成的Docker配置。
##安全

高级安全配置已经超过了此概念性项目的范围。为了更真实地模拟真实系统,请考虑使用https和JCE密钥库来加密微服务密码和配置服务器的properties内容(有关详细信息,请参阅文档)。
#基础设施自动化

部署微服务比部署单一的应用的流程要复杂得多,因为它们相互依赖。拥有完全基础设置自动化是非常重要的。我们可以通过持续交付的方式获得以下好处:

* 随时发布软件的能力。
* 任何构建都可能最终成为一个发行版本。
* 构建工件(artifact)一次,根据需要进行部署。

这是一个简单的持续交付工作流程,在这个项目的实现:

在此配置中,Travis CI为每一个成功的Git推送创建了标记镜像。因此,每一个微服务在Docker Hub上的都会有一个latest镜像,而较旧的镜像则使用Git提交的哈希进行标记。如果有需要,可以轻松部署任何一个,并快速回滚。
5.png

#如何运行全部?
这真的很简单,我建议您尝试一下。请记住,您将要启动8个Spring Boot应用、4个MongoDB实例和RabbitMq。确保您的机器上有4GB的内存。您可以随时通过网关、注册中心、配置、认证服务和账户中心运行重要的服务。

运行之前

* 安装Docker和Docker Compose。
* 配置环境变量:CONFIG_SERVICE_PASSWORD, NOTIFICATION_SERVICE_PASSWORD, STATISTICS_SERVICE_PASSWORD, ACCOUNT_SERVICE_PASSWORD, MONGODB_PASSWORD

生产模式

在这种模式下,所有最新的镜像都将从Docker Hub上拉取。只需要复制docker-compose.yml并执行docker-compose up -d即可。

开发模式

如果您想自己构建镜像(例如,在代码中进行一些修改),您需要克隆所有仓库(repository)并使用Mavne构建工件(artifact)。然后,运行docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

docker-compose.dev.yml继承了docker-compose.yml,附带额外配置,可在本地构建镜像,并暴露所有容器端口以方便开发。

重要的端点(Endpoint)

* localhost:80 —— 网关
* localhost:8761 —— Eureka仪表盘
* localhost:9000 —— Hystrix仪表盘
* localhost:8989 —— Turbine stream(Hystrix仪表盘来源)
* localhost:15672 —— RabbitMq管理

注意

所有Spring Boot应用都需要运行配置服务器才能启动。得益于Spring Boot的fail-fast属性和docker-compsoe的restart:always选项,我们可以同时启动所有容器。这意味着所有依赖的容器将尝试重新启动,直到配置服务器启动运行为止。

此外,服务发现机制在所有应用启动后需要一段时间。在实例、Eureka服务器和客户端在其本地缓存中都具有相同的元数据之前,任何服务都不可用于客户端发现,因此可能需要3次心跳。默认的心跳周期为30秒。

原文链接:Microservice Architectures With Spring Cloud and Docker(翻译:Oopsguy

秦苍科技是如何管理数百个微服务并避免踩坑的?

尼古拉斯 发表了文章 • 0 个评论 • 2567 次浏览 • 2017-09-25 20:30 • 来自相关话题

【编者的话】过去两年中,微服务架构是一个非常热门的技术名词。秦苍科技也在微服务方面做了大量的投资和实践,我们有开发、测试、准生产、生产四套环境,每套环境有230+个微服务,总共有近1000个微服务。 秦苍科技为什么要采用微服务的架构? ...查看全部
【编者的话】过去两年中,微服务架构是一个非常热门的技术名词。秦苍科技也在微服务方面做了大量的投资和实践,我们有开发、测试、准生产、生产四套环境,每套环境有230+个微服务,总共有近1000个微服务。

秦苍科技为什么要采用微服务的架构?如何管理这么多微服务?本文将对这些问题进行阐述,希望对正在踩坑路上和即将踩坑的朋友们有所帮助。
#为什么要使用微服务
关于微服务架构优点有很多讨论。但是,个人认为许多优点都可以算作一些“伪优点”。例如:

* 从单个服务的角度而言,微服务的每个服务都很简单,只关注于一个业务功能,降低了单个服务的复杂性。但是,从整体而言,作为一种分布式系统,微服务引入额外的复杂性和问题,比如说网络延迟、容错性、异步、分布式事务等。
* 从单个服务的角度而言,每个微服务可以通过不同的编程语言与工具进行开发,针对不同的服务采用更加合适的技术,也可以快速地尝试一些新技术。
* 但是,从整个公司的角度来说,往往希望能够尽量统一技术栈,避免重复投资某些技术。假设某公司主要用Spring Boot和Spring Cloud来构建微服务,使用Netflix Hystrix作为服务熔断的解决方案。后来,一些微服务开始使用Node.js来实现。很快,该公司就发觉使用Node.js构建的服务无法使用已有的服务熔断解决方案,需要为Node.js技术栈重新开发。
* ……

我的观点是微服务架构的核心就是解决扩展性的问题。从组织结构的角度来看,微服务架构使得研发部门可以快速扩张,因为每个微服务都不是特别复杂,工作在这个服务上的研发人员不是必须对整个系统都充分了解,很多新人可以快速上手。

从技术的角度来看,微服务架构使得每个微服务可以独立部署、独立扩展,可以根据每个服务的规模来部署满足需求的规模,选择更适合于服务资源需求的硬件。

秦苍科技正处在一个人员规模和业务规模快速扩张的阶段,微服务的扩展性非常贴切地满足了我们现阶段的需求,所以使用微服务架构对秦苍科技来说也变成了一件顺理成章的事情了。
#如何进行服务管理
随着服务数量的增多,我们发觉微服务间的依赖关系越来越复杂,一个服务的改变将会波及多个服务,错误排查也相当困难。当系统有几百个服务时,这种依赖简直就是一个噩梦。

所以,秦苍科技启动了服务治理的项目,使用服务注册和发现技术简化服务的管理,对服务进行了分组、分层,降低系统的复杂性和耦合性。

其实,服务的管理和人员组织结构的管理非常类似。当一个组织中成员增多时,我们会将人员分为若干个小的团队,每个团队由较少的人员组成,负责某个比较独立的业务,并且会有一个团队负责人负责和其他团队的沟通。

当组织中的成员进一步增多时,我们会将若干个团队合并为一个部门,每个部门负责某个独立的职能。

对于微服务的管理,我们采用与组织结构管理类似的方法,把彼此紧密相关的服务构建成逻辑上的一个组。类似于组织结构中的团队负责人,该组有一个API网关,向外暴露了组中所有服务的功能。对于该组中服务的使用方来说,都通过这个API网关进行访问,仿佛这个组就是一个服务一样,无需关心该组是由多少个服务组成。

通过分组的方式,秦苍230+个微服务变为了25个组,从而大大降低了系统逻辑上的复杂性。然后,我们把系统分为了若干层,每一层由若干个组组成。上层只可以调用下层的服务,下层不可以调用上层服务。通过分层的方式,我们降低了系统的耦合性。
01.jpg

图 1 服务分层的组织方式
#如何让服务管理自动化?
在人员组织结构管理中,为了高效地管理人员的信息,通常会引入一套系统管理这些信息,例如:微软的Active Directory。

当一个新员工入职的时候,我们会在这个系统中添加该成员的基本信息,例如:姓名、电话、email等信息。

在一个员工离职的时候,我们会在这个系统中删除该员工。当一个员工A要和员工B沟通交流的时候,员工A可以根据员工B的姓名在这个系统中查询出员工B的电话、email等信息,然后使用电话或email和员工B进行沟通。

对于微服务的管理,我们也希望有这样一个系统,能够注册和查询微服务的所有信息。微服务架构中把这个系统叫做服务注册中心。

秦苍科技采用了Netflix Eureka作为我们的服务注册中心,所有的微服务都基于Spring Boot和Spring Cloud进行构建。

系统中的每个服务都非常“聪明”,在启动后都会跑到服务注册中心“自报家门”,告诉服务注册中心自己的名字、IP地址、版本、状态、所属组、所属层、所属层的级别、依赖的微服务等信息,服务注册中心会将这些服务保存到它的“花名册”上。

通过服务注册中心的“花名册”,我们可以对系统一览无遗,轻松了解系统的每一层,每一层中所有组,每个组中所有服务的信息。

当服务A依赖于服务B时,它只要知道服务B的名称,就可以从服务注册中心的“花名册”中查询到服务B的所有实例,及其相关的所有信息,例如IP地址、所属组、所属层等信息。

这样,服务A不再需要“死记硬背”服务B的IP地址。当服务A调用服务B的时候,服务A首先从服务注册中心获取到服务B的所有实例,然后服务A采用某种策略从服务B的实例中选择其中一个实例将请求发送给该实例,从而实现了客户端负载均衡。

这个客户端负载均衡的功能就是由图2中的Netflix Ribbon模块来完成的。
02.jpg

图 2 服务注册和发现机制
#如何自动控制服务依赖?
使用前述的方法后,我们可以使用分组分层的方式对服务进行管理。但是,我们仍然需要一定的技术手段来保证开发人员在开发某个微服务的时候一定会遵守下层服务不能调用上层服务的原则,保证开发人员不会引入不该引入的微服务依赖。

秦苍实现自动控制服务依赖的核心是注册服务依赖信息到服务注册中心,扩展Netflix Ribbon限制服务调用。

使用前述的方法后,我们可以使用分组分层的方式对服务进行管理。但是,我们仍然需要一定的技术手段来保证开发人员在开发某个微服务的时候一定会遵守下层服务不能调用上层服务的原则,保证开发人员不会引入不该引入的微服务依赖。

秦苍实现自动控制服务依赖的核心是注册服务依赖信息到服务注册中心,扩展Netflix Ribbon限制服务调用。
##服务注册依赖信息到注册中心
每个微服务都会注册该服务所属层、所属层级、依赖的微服务等信息到服务注册中心。这些服务依赖信息作为服务的配置项,保存在配置文件中,统一由运维人员管理。开发人员在开发环境中可以修改这些服务依赖信息,进行开发调试。

但是,在测试、准生产和生产环境中,运维人员会使用自己管理的配置项覆盖掉这些信息,运维只有在经过架构师同意后才会修改这些服务依赖信息。

这就意味着开发无法绕过架构师自行引入新的依赖,否则在测试、准生产和生产环境中服务是调不通的,代码无法正常工作,这样就从技术手段上保证了无法随意地引入微服务依赖。
##扩展Netflix Ribbon限制服务调用
有了服务依赖信息后,服务调用时我们需要使用这些信息限制不允许的服务调用。只要对Ribbon进行少许扩展就可以满足这样的需求。

本质来讲,Ribbon就是一个服务调用的“路由器”。只要在这个“路由器”上定义一些新的规则,我们就可以控制服务的调用关系。例如:

白名单规则:对于一个微服务,只能调用在该服务白名单列表中的服务,否则调用失败。

层级调用规则:对于一个微服务,只能调用层级比自己低的服务,否则调用失败。

……

目前,秦苍科技对Spring Boot Admin进行了扩展来构建自己的服务治理中心,用户可以按照组的方式浏览服务,查看每个服务的健康状态、配置信息、日志等。
03.png

图 3 服务治理中心

将来,我们打算通过读取服务注册中心中每个服务注册的元信息,在服务治理中心中自动画出整个系统的架构图。关于自动管控服务依赖方面,我们的工作还在进行中。

希望将来有一天,我们在微服务治理方面的积累足够成熟,可以将这些经验回馈给开源社区。

作者介绍:李荣陆,秦苍科技高级技术总监兼首席架构师。复旦大学计算机博士。曾在Autodesk、SAP、Blackboard等公司担任首席工程师、架构师、高级经理等职位。在国内、国际权威杂志和会议上发表十余篇论文,出版过一本著作。十几年来在云计算、计算机辅助设计、自然语言处理、搜索引擎、数据挖掘、人工智能、机器学习、微服务架构等领域均有涉猎。

原文链接:如何管理数百个微服务并避免踩坑?

Spring Cloud在国内中小型公司能用起来吗?

尼古拉斯 发表了文章 • 0 个评论 • 2958 次浏览 • 2017-09-12 19:25 • 来自相关话题

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring B ...查看全部
今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring Boot和Spring Cloud近两年的程序员,看的我手痒痒不答不快呀。
#好问题
好问题必须配认真的回答,仔细的看了题主的问题,发现这个问题非常具有代表性,可能是广大网友想使用Spring Cloud却又对Spring Cloud不太了解的共同想法,题主对Spring Cloud使用的方方面面都进行过了思考,包括市场,学习、前后端、测试、配置、部署、开发以及运维,下面就是题主原本的问题:

想在公司推广Spring Cloud,但我对这项技术还缺乏了解,画了一张脑图,总结了种种问题。

01.jpg

微服务是这样一个结构吗:



前端或二方 - > ng集群 -> zuul集群 -> eureka-server集群 -> service provider集群
(二方指其他业务部门)



想要明白这个问题,首先需要知道什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系?
#什么是Spring Boot?
Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只需要很少的Spring配置。

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架(不知道这样比喻是否合适)。

Spring Boot的核心思想就是约定大于配置,一切自动完成。采用Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。如果你对Spring Boot完全不了解,可以参考我的这篇文章:《SpringBoot(一):入门篇-%E5%85%A5%E9%97%A8%E7%AF%87.html)》。
#什么是Spring Cloud?
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud做为大管家就需要提供各种方案来维护整个生态。

Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能,如果你对Spring Cloud组件不是特别了解的话,可以参考我的这篇文章:《Spring Cloud(一):大话Spring Cloud》。
#Spring Boot和Spring Cloud的关系
Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。

Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

Spring -> Spring Boot > Spring Cloud 这样的关系。
#回答

以下为我在知乎的回答。

首先楼主问的这些问题都挺好的,算是经过了自己的一番思考,我恰好经历了你所说的中小公司,且都使用Spring Cloud并且已经投产上线。第一家公司技术开发人员15人左右,项目实例 30多,第二家公司开发人员100人左右,项目实例达160多。

实话说Spring Boot、Spring Cloud仍在高速发展,技术生态不断的完善和扩张,不免也会有一些小的bug,但对于中小公司的使用来将,完全可以忽略,基本都可以找到解决方案,接下来回到你的问题。
##1、市场
据我所知有很多知名互联网公司都已经使用了Spring Cloud,比如阿里、美团但都是小规模,没有像我经历的这俩家公司,业务线全部拥抱Spring Cloud;另外Spring Cloud并不是一套高深的技术,普通的Java程序员经过一到俩个月完全就可以上手,但前期需要一个比较精通人的来带队。
##2、学习
有很多种方式,现在Spring Cloud越来越火的情况下,各种资源也越来越丰富,查看官方文档和示例,现在很多优秀的博客在写spirng cloud的相关教程,我这里收集了一些Spring Boot和Spring Cloud的相关资源可以参考,找到博客也就找到人和组织了。
##3、前后职责划分
其实这个问题是每个系统架构都应该考虑的问题,Spring Cloud只是后端服务治理的一套框架,唯一和前端有关系的是thymeleaf,Spring推荐使用它做模板引擎。一般情况下,前端App或者网页通过Zuul来调用后端的服务,如果包含静态资源也可以使用Nginx做一下代理转发。
##4、测试
Spring-boot-starter-test支持项目中各层方法的测试,也支持controller层的各种属性。所以一般测试的步奏是这样,首先开发人员覆盖自己的所有方法,然后测试微服务内所有对外接口保证微服务内的正确性,再进行微服务之间集成测试,最后交付测试。
##5、配置
session共享有很多种方式,比如使用tomcat sesion共享机制,但我比较推荐使用Redis缓存来做session共享。完全可以分批引入,我在上一家公司就是分批过渡上线,新旧项目通过Zuul进行交互,分批引入的时候,最好是新业务线先使用Spring Cloud,老业务做过渡,当完全掌握之后在全部替换。如果只是请求转发,Zuul的性能不一定比Nginx低,但是如果涉及到静态资源,还是建议在前端使用Nginx做一下代理。另外Spring Cloud有配置中心,可以非常灵活的做所有配置的事情。
##6、部署
多环境不同配置,Spring Boot最擅长做这个事情了,使用不同的配置文件来配置不同环境的参数,在服务启动的时候指明某个配置文件即可,例如:java -jar app.jar --spring.profiles.active=dev就是启动测试环境的配置文件;Spring Cloud 没有提供发布平台,因为Jenkins已经足够完善了,推荐使用jenkins来部署Spring Boot项目,会省非常多的事情;灰度暂时不支持,可能需要自己来做,如果有多个实例,可以一个一个来更新;支持混合部署,一台机子部署多个是常见的事情。
##7、开发
你说的包含html接口就是前端页面吧,Spring Boot可以支持,但其实也是Spring Mvc在做这个事情,Spring Cloud只做服务治理,其它具体的功能都是集成了各种框架来解决而已;excel报表可以,其实除过swing项目外,其它Java项目都可以想象;Spring Cloud和老项目可以混合使用,通过Zuul来支持。是否支持callback,可以通过MQ来实现,还是强调Spring Cloud只是服务治理。
##8、运维
Turbine、Zipkin可以用来做熔断和性能监控;动态上下线某个节点可以通过Jenkins来实现;Provider下线后,会有其它相同的实例来提供服务,Eureka会间隔一段时间来检测服务的可用性;不同节点配置不同的流量权值目前还不支持。注册中心必须做高可用集群,注册中心挂掉之后,服务实例会全部停止。

总结,中小企业是否能用的起来Spring Cloud,完全取决于自己公司的环境,如果是一个技术活跃型的团队就大胆的去尝试吧,目前Spring Cloud是所有微服务治理中最优秀的方案,也是一个趋势,未来一两年可能就会像Spring一样流行,早接触早学习岂不更好。

希望能解答了你的疑问。
#Spring Cloud 架构
我们从整体来看一下Spring Cloud主要的组件,以及它的访问流程:
02.jpg


* 外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务
* 网关接收到请求后,从注册中心(Eureka)获取可用服务
* 由Ribbon进行均衡负载后,分发到后端的具体实例
* 微服务之间通过Feign进行通信处理业务
* Hystrix负责处理服务超时熔断
* Turbine监控服务间的调用和熔断相关指标

图中没有画出配置中心,配置中心管理各微服务不同环境下的配置文件。

以上就是一个完整的Spring Cloud生态图。

最后送一个完整示例的Spirng Cloud开源项目等你去拿:https://github.com/ityouknow/spring-cloud-examples

原文链接:https://juejin.im/post/59b72f865188257e8153732d

Docker with Spring Boot

nkduqi 发表了文章 • 2 个评论 • 5659 次浏览 • 2016-03-06 15:27 • 来自相关话题

前段时间在我厂卷爷的指导下将Docker在我的实际项目中落地,最近几个小demo都尽量熟悉docker的使用,希望通过这篇文章分享我截止目前的使用经验(如有不准确的表述,欢迎帮我指出)。本文的主要内容是关于Java应用程序的docker化,首先简单介绍了doc ...查看全部
前段时间在我厂卷爷的指导下将Docker在我的实际项目中落地,最近几个小demo都尽量熟悉docker的使用,希望通过这篇文章分享我截止目前的使用经验(如有不准确的表述,欢迎帮我指出)。本文的主要内容是关于Java应用程序的docker化,首先简单介绍了docker和docker-compose,然后利用两个案例进行实践说明。

简单说说Docker,现在云计算领域火得一塌糊涂的就是它了吧。Docker的出现是为了解决PaaS的问题:运行环境与具体的语言版本、项目路径强关联,因此干脆利用lxc技术进行资源隔离,构造出跟随应用发布的运行环境,这样就解决了语言版本的限制问题。PaaS的出现是为了让运维人员不需要管理一台虚拟机,IaaS的出现是为了让运维人员不需要管理物理机。云计算,说到底都是俩字——运维。

云计算领域的技术分为虚拟化技术和资源管理两个方面,正好对应我们今天要讲的两个工具:Docker和docker-compose。Docker的主要概念有:容器、镜像、仓库;docker-compose是fig的后续版本,负责将多个docker服务整合起来,对外提供一致服务。

1. Spring Boot应用的docker化
首先看Spring Boot应用程序的docker化,由于Spring Boot内嵌了tomcat、Jetty等容器,因此我们对docker镜像的要求就是需要java运行环境。我的应用代码的的Dockerfile文件如下:

```
#基础镜像:仓库是java,标签用8u66-jdk
FROM java:8u66-jdk
#当前镜像的维护者和联系方式
MAINTAINER duqi duqi@example.com
#将打包好的spring程序拷贝到容器中的指定位置
ADD target/bookpub-0.0.1-SNAPSHOT.jar /opt/bookpub-0.0.1-SNAPSHOT.jar
#容器对外暴露8080端口
EXPOSE 8080
#容器启动后需要执行的命令
CMD java -Djava.security.egd=file:/dev/./urandom -jar /opt/bookpub-0.0.1-SNAPSHOT.jar
```
因为目前的示例程序比较简单,这个dockerfile并没有在将应用程序的数据存放在宿主机上。如果你的应用程序需要写文件系统,例如日志,最好利用`VOLUME /tmp`命令,这个命令的效果是:在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录。

把这个Dockerfile放在项目的根目录下即可,后续通过`docker-compose build`统一构建:基础镜像是只读的,然后会在该基础镜像上增加新的可写层来供我们使用,因此java镜像只需要下载一次。

docker-compose是用来做docker服务编排,参看《Docker从入门到实践》中的解释:

Compose 项目目前在 Github 上进行维护,目前最新版本是 1.2.0。Compose 定位是“defining and running complex applications with Docker”,前身是 Fig,兼容 Fig 的模板文件。



Dockerfile 可以让用户管理一个单独的应用容器;而 Compose 则允许用户在一个模板(YAML 格式)中定义一组相关联的应用容器(被称为一个 project,即项目),例如一个 Web 服务容器再加上后端的数据库服务容器等。



单个docker用起来确实没什么用,docker技术的关键在于持续交付,通过与jekins的结合,可以实现这样的效果:开发人员提交push,然后jekins就自动构建并测试刚提交的代码,这就是我理解的持续交付。

2. spring boot + redis + mongodb
在这个项目中,我启动三个容器:web、redis和mongodb,然后将web与redis连接,web与mongodb连接。首先要进行redis和mongodb的docker化,redis镜像的Dockerfile内容是:

```
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get -y install redis-server
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
```
Mongodb镜像的Dockerfile内容是,docker官方给了mongodb的docker化教程,我直接拿来用了,参见Dockerizing MongoDB

```
# Format: FROM repository[:version]
FROM ubuntu:14.04
# Format: MAINTAINER Name
MAINTAINER duqi duqi@example.com
# Installation:
# Import MongoDB public GPG key AND create a MongoDB list file
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
RUN echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list
# Update apt-get sources AND install MongoDB
RUN apt-get update && apt-get install -y mongodb-org
# Create the MongoDB data directory
RUN mkdir -p /data/db
# Expose port 27017 from the container to the host
EXPOSE 27017
# Set usr/bin/mongod as the dockerized entry-point application
ENTRYPOINT ["/usr/bin/mongod"]```


使用docker-compose编排三个服务,具体的模板文件如下:
```
web:
build: .
ports:
- "49161:8080"
links:
- redis
- mongodb
redis:
image: duqi/redis
ports:
- "6379:6379"
mongodb:
image: duqi/mongodb
ports:
- "27017:27017"
```

架构比较简单,第一个区块的build,表示docker中的命令“docker build .”,用于构建web镜像;ports这块表示将容器的8080端口与宿主机(IP地址是:192.168.99.100)的49161对应。因为现在docker不支持原生的osx,因此在mac下使用docker,实际上是在mac上的一台虚拟机(docker-machine)上使用docker,这台机器的地址就是192.168.99.100。参见:在mac下使用docker

links表示要连接的服务,redis与下方的redis区块对应、mongodb与下方的mongodb区块对应。redis和mongodb类似,首先说明要使用的镜像,然后规定端口映射。

那么,如何运行呢?
  1. 命令`docker-compose build`,表示构建web服务,目前我用得比较土,就是编译jar之后还需要重新更新docker,优雅点不应该这样。

  1. 命令`docker-compose up`,表示启动web服务,可以看到mongodb、redis和web依次启动,启动后用`docker ps`查看当前的运行容器。


特别注意,在配置文件中写redis和mongodb的url时,要用虚拟机的地址,即192.168.99.100。例如,redis的一个配置应该为:spring.redis.host=192.168.99.100。

3. spring boot + mysql
拉取mysql镜像的指令是:`docker run --name db001 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin -d mysql:5.7`,表示启动的docker容器名字是db001,登录密码一定要设定, -d表示设置Mysql版本。

我的docker-compose模板文件是:

```
web:
build: .
ports:
- "49161:8080"
links:
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: springbootcookbook
ports:
- "3306:3306"
```

主要内容跟之前的类似,主要讲下mysql部分,通过environement来设置进入mysql容器后的环境变量,即连接数据库的密码MYSQL_ROOT_PASSWORD,使用的数据库名称MSYQL_DATABASE等等。

一直想写这篇文章做个总结,写来发现还是有点薄,对于docker我还需要系统得学习,不过,针对上面的例子,我都是亲自实践过的,大家有什么问题可以与我联系。

参考资料
  1. Docker从入门到实践
  2. Docker - Initialize mysql database with schema
  3. 使用Docker搭建基础的mysql应用
  4. Spring Boot with docker

----
作者介绍
杜琪,现就职于阿里巴巴,研发工程师,负责Yunos系统云服务-PMS(个人移动服务),目前的研究方向是Spring Boot、微服务、Docker等技术。热爱写作,倡导写作驱动学习,个人技术博客地址:link

Spring Boot与Docker(四):额外的微服务、更新容器、Docker Compose和负载均衡

国会山上的猫TuxHu 发表了文章 • 0 个评论 • 7227 次浏览 • 2015-12-15 23:11 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adba ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治●华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

现在我们对于微服务和Docker有了扎实的了解,启动了一个MongoDB容器和Spring Boot微服务容器并且借助于容器的link机制(参考我们的Git版本库第四部分开始部分)实现了它们之间的相互通讯。为了完成我们最初的用例,我们需要两个微服务——分别是“missions”和“rewards”。我将开始这个话题并按照与我们之前构建employee微服务相同的方式来构建这两个微服务,可以参考Git版本库第四部分的第一步来获得这两个微服务容器。现在如果我们执行docker ps,我们将看到如下的信息,其中的一些列为了简洁被移掉了:
CONTAINER ID IMAGE                      PORTS                         NAMES
86bd9bc19917 microservicedemo/employee 0.0.0.0:32779->8080/tcp employee
1c694e248c0a microservicedemo/reward 0.0.0.0:32775->8080/tcp reward
c3b5c56ff3f9 microservicedemo/mission 0.0.0.0:32774->8080/tcp mission
48647d735188 mongo 0.0.0.0:32771->27017/tcp mongodb


更新镜像
这都是很简单的,但是没有太大的作用,因为此时没有一个微服务可以在简单的数据CRUD功能之外带来任何直接的价值。让我们开始对一些代码的更改进行分层,以提供更多的价值和功能。我们会对某个微服务做出一些改变,然后处理如何更新镜像,了解对于容器的版本控制。由于员工通过完成任务来获得积分,我们需要追踪他们的任务完成情况、积分总计(获得和活跃的)以及奖励兑换。我们将添加一些额外的类到Employee模型中——这些不是顶层的业务对象,所以它们不会有它们自己的微服务,但是将会在Employee对象中提供上下文内容。一旦这些变化做出了(参见Git版本库第四部分第二步),将会有一些结构性的改变,需要在整个软件栈中同步,更新镜像的步骤如下:
● 重新编译源代码
gradle build

● 重新构建镜像
docker build -t microservicedemo/employee .

最后将会看到一些如下的消息:
Removing intermediate container 5ca297c19885 Successfully build 088558247

● 现在我们需要删除旧容器,替换为新的:
docker stop employee
docker rm employee
docker run -P -d --name employee --link mongodb microservicedemo/employee


需要注意的重要事项就是在运行容器内的代码是不会更新的,容器和微服务的另外一个核心原则是容器内部的代码和配置是不可变的。换句话说,你不用更新容器,只需要替换它。这对于一些容器的使用案例会造成一些问题,比如使用容器来操作数据库或者其它的持久化资源。

使用Docker Compose来编排容器
像我一样,如果你在本系列文章之间有其他的工作要做,如何确保所有的各种命令行参数都可以来连接这些容器,可能有点令人沮丧。编排这一队容器就是Docker Compose(以前被成为Fig)的目的。在Yaml配置文件中定义你的一组容器,并且管理这些容器的运行时配置。在很多方面,可以把Docker Compose想象成为一个编排者,确保“正运行”的容器有着正确的选项和配置。我们将通过命令行参数为我们的应用创建一个这样的编排者来做所有管理想做的事情。
docker-compose.yml:
employee:
build: employee
ports:
- "8080"
links:
- mongodb
reward:
build: reward
ports:
- "8080"
links:
- mongodb
mission:
build: mission
ports:
- "8080"
links:
- mongodb
mongodb:
image: mongo


接着在命令行上敲入:
docker-compose up -d


然后整个一队容器都将会启动,非常方便!许多Docker命令在Docker-Compose上都有类似的命令,如果我们运行"`docker-compose ps`",我们会看到:

Name Command State Ports
-------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp


容器的动态伸缩和负载均衡
不过上述这些还不是Docker Compose可以做的所有工作,如果你运行“`docker-compose scale [compose container name]=3`”,,将会创建多个容器实例。比如运行“`docker-compose scale employee=3`”,接着运行“`docker-compose ps`”,将会看到:

Name Command State Ports
---------------------------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_employee_2 java -Dspring.data.mongodb ... Up 0.0.0.0:32791->8080/tcp
git_employee_3 java -Dspring.data.mongodb ... Up 0.0.0.0:32790->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp

我们的employee容器现在有了三个实例!Docker Compse记得你设置的数量,所以下次运行的时候,将会启动三个employee容器实例,就我个人而言,我认为这个应该在docker-compose.yml文件中设置,但是其实不是的。

希望你开始看到一个问题如何发展的。我们应该如何构建一个针对终端用户的事实上使用微服务的应用呢?因为容器的端口变了,并且在一个Docker集群服务器环境中(比如Docker Swarm),宿主机的IP地址也会改变。有一些先进的解决方案(Kubernetes和AWS的ECS),但是现在我们将寻找一个相对简单的选项,将采用一个非常容易的方法来对容器实例做负载均衡。Tutum是一家构造多重云容器组织能力的公司,已经给Docker社区提交了一个HAProxy的扩展插件,这个扩展插件会基于连接的容器自动配置其自身。让我们为多个employee容器实例添加一个负载均衡器,我们将其添加到docker-compose.yml文件中:

ha_employee:
image: tutum/haproxy
links:
- employee
ports:
- "8080:80"


接着我们运行“`docker-compose up -d`”,将会下载缺失的镜像并且启动容器。现在我们可以再次在特定的端口(8080)运行测试,这次将会对于所有运行的employee容器进行负载均衡。接着,我们可以在192.168.99.100:8080上敲击employee服务集群,默认情况下将会轮循这三个实例。易如反掌!HAProxy的Docker容器还有许多额外的特性和功能点,我建议可以从https://github.com/tutumcloud/haproxy获得更多的信息。

HAProxy对于一个特定容器的多个实例的负载均衡处理地非常巧妙,是单容器环境的理想选择。然而,我们没有这样的环境,那咋办?我们可以启动多个HAProxy实例来处理容器集群,在宿主机的不同端口上转发每一个HAProxy容器端口,所以我们的employee服务放在了8080端口,mission服务放在8081端口,reward服务放在8082端口(参考Git版本库第四部分第三步)。如果我们来到生产环境,我们可以利用Nginx来创建反向代理,将所有的服务请求转发到一个单独的IP地址和端口上(通过URL路径/employee/和/reward/路由到相应的容器)。或者我们可以使用一个更加健壮的服务发现路由,比如这儿的利用etcd和一些让人印象深刻的Docker元数据脚本和模板引擎,来自于Jason Wilder的Docker-gen系统(https://hub.docker.com/r/jwilder/docker-gen/),以及大量的额外的自我管理的服务发现的解决方案。我们目前将会保留这个简单的HAProxy解决方案,因为它给与了我们一个对于如何管理容器集群的扎实的理解。

这是一个结束本系列的好地方。我可以说还有有许多额外的领域还没有涉及到,包括:
● 构建一个前端的容器,或者一个移动APP容器
● 包含后端的数据批处理过程
● 动态分级的容器集群来处理消息队列的条目
● 将服务从Java/Spring Boot迁移到Scala/Akka/Play
● 建立持续集成
● 构造自己的镜像Repository或者使用容器Repository服务(Google或者Docker Hub)
● 评估容器管理系统比如AWS的ECS或者Kubernetes


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART IV(翻译:胡震)

Spring Boot与Docker(三):构建你的第一个微服务和相关容器以及容器的连接

国会山上的猫TuxHu 发表了文章 • 2 个评论 • 5167 次浏览 • 2015-12-14 00:05 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包 ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治·华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

本篇构建微服务的步骤如下:

* 建立一个新的Spring Boot工程
* 定义我们的员工对象
* 持久连接
* 公开Web服务
* 定义一个Docker容器来运行我们的微服务,包括连接到我们在第二篇中创建的Mongo容器
* 在稍早设置的Docker Machine中运行我们的容器

建立我们的Spring Boot工程
我们将在我们的工程根目录下创建一个文件夹(生产环境中是在一个单独的版本库中)来承载我们的服务,在这个文件夹中,我们将创建build.grade文件。Spring Boot的优势就是在于对于依赖定义非常纯粹,如魔法般地带来了大量的互操作性。我们的build.grade文件看起来如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.RELEASE'
}
}
apply plugin: 'spring-boot'

repositories { jcenter()
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-web"
}

现在,正如我们提到的,我们将使用IDE,具体地说就是Spring Tool Suite(STS),所以我们将增加对于Gradle的支持。首先,打开STS,接着在打开的Dashboard页面上点击“IDE Extensions”的链接:
403x220xmicroservice-part-3-pic-1.jpg.pagespeed_.ic_.XIVhOVlbxA_.jpg

在屏幕上选择“Grade Support”,点击“Install”,按照提示完成安装,其中包括重启STS:
468x127xmicroservice-part-3-pic-2.jpg.pagespeed_.ic_.K4Ui_72tTm_.jpg

重启之后,选择“Import project…”,接着选择Gradle project(现在可用了),指向你的目录,然后点击“Build Model”——这将会用“Employee”来产生项目列表——选择这个列表并且点击“Finish”,这将会导入一个简单的build.grade到我们启动的工程。一旦工程被导入之后,第一个将是Spring Boot配置类,在这个场景中,我们将按照他们的服务来命名,所以在这种情况下配置类将会命名为EmployeeBoot。Spring大量使用了注解和反射,所以最小的配置也是需要的(忽略导入):
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class EmployeeBoot {

public static void main(String[] args) {
SpringApplication.run(EmployeeBoot.class);
}
}

我们的Employee类
接下来,我们将使得我们的POJO类来持有员工信息,我们目前保持最少的字段,有必要的话我们将增加:
Employee.Class

@Document(collection=”employees”)
public class Employee {

@Id
private String id;

private String email;
private String fullName;
private String managerEmail;

// getters and setters omitted for brevity
}

注意@Document这个注解——这会将这个对象连结为一个Mongo文档,并为该Employee“文档”在哪里存储指定了集合名字。
接下来我们将定义一个Spring持久类来读取这些Employee:
EmployeeRepository.class:
public interface EmployeeRepository extends
MongoRepository {
}

Spring框架的一个优美之处就是在于所有你需要写的——比如扩展自MongoRepository的接口甚至不需要手动编写实现代码。两个通用的参数中第一个表示需要持久化的对象类型(Employee),第二个表示唯一的标识符类型(String类型)。Spring框架将会自动提供所声明的功能95%的实现。接下来的问题就是:Spring如何知道数据库的位置?Spring默认情况下将会寻找localhost:27017,这显然是不会工作的,所以我们需要直接设置位置新型。我们可以实现自己的Mongo模板Bean,但是幸运的是Spring允许我们通过Java属性表传递连接信息。我们可以定义一个属性文件或者在命令行上传递进来。我们选择了后者因为当稍后构建我们的容器时命令行方式非常方便。最后我们需要创建的是一个或者两个Rest端点并确保它们可以工作。我们将构建一个快速的Spring Controller,然后我们就可以测试了。
EmployeeController.java

@RestController
@RequestMapping("/employee")
public class EmployeeController {

@Autowired
EmployeeRepository employeeRepository;

@RequestMapping(method = RequestMethod.POST)
public Employee create(@RequestBody Employee employee){

Employee result = employeeRepository.save(employee);
return result;
}

@RequestMapping(method = RequestMethod.GET, value="/{employeeId}")
public Employee get(@PathVariable String employeeId){
return employeeRepository.findOne(employeeId);
}
}

最开始的@RestController和@RequestMapping这两个类级别的注解告诉Spring框架需要公开这是一个接收JSON的Rest服务,并且公开了URI路径是/employee。@Autowired注解告诉Spring框架采用上面我们定义的Repository接口的自动生成实现代码并将其注入到这个Controller中。现在到了特定的操作——方法级别的@RequestMapping注解表明这个方法将会基于HTTP动词来使用(在本例中是POST和GET),另外对于GET操作,我们指明了一个URL路径{employee},比如使用/employee/abcd1234来寻找一个员工并且返回该值。
现在我们有了足够多的准备工作可以来测试了,第一点,我们需要编译和运行我们的Spring Boot应用,在Eclipse中有很多方法可以做到这一点,但是我们将从命令行开始,并以我们的方式工作。在你的Employee目录中,敲入:`gradle build`,这个命令将会编译Spring Boot应用到build/lib/Employee.jar,这个jar包包含了运行这个应用所需要的所有东西,包括一个嵌入式的Servlet容器(默认情况下是Tomcat)。
在我们运行和测试这个应用之前,我们需要稍作回顾——我们的Mongo服务又在哪里?观察“docker ps”命令的输出,我们记得虚拟机的32777端口映射到了Mongo容器的27017端口,虚拟机的IP地址是192.126.99.100。如前所述,我们可以通过传递一个环境变量属性的方式来把连接属性传递给Spring,所以运行这个应用的命令行如下:
java -Dspring.data.mongodb.uri=mongodb://192.168.99.100:32777/micros -jar build/libs/Employee.jar

一旦应用启动之后(应该在1~4秒),你可以使用选项中的Rest工具来打开Web服务。

456x171xmicroservice-part-3-pic-3.jpg.pagespeed_.ic_.wWMISBC68u_.jpg

不要忘了在HTTP头部包含值为“application/json”的“Content-Type”,你应该收到如下的响应(id的值取决于你自己的情况):
{
"id": "55fb2f1930e07c6c844b02ff",
"email": "dan.greene@3pillarglobal.com",
"fullName": "Daniel Greene",
"managerEmail": null
}

你可以通过如下的调用测试我们的GET方法:
http://localhost:8080/employee/55fb2f1930e07c6c844b02ff

你应该得到相同的文档,万岁!我们的服务起作用了!

将Boot应用导入容器
现在我们需要构建我们的第一个容器来运行我们的Employee微服务。我们的途径是定义一个Dockerfile,这个文件将会定义如何生成一个镜像并放入我们的精益求精、短小精悍的微服务中。让我们阅读这个文件并逐步解析(参考第三篇第三步请跳到这儿):
FROM java:8
VOLUME /tmp
ADD build/libs/Employee.jar app.jar
EXPOSE 8080
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Dspring.data.mongodb.uri=mongodb://mongodb/micros", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]


* 我们从一个包含了安装了Java 8的标准镜像(镜像名字为“Java”,标签为“8”)开始构建过程
* 我们接着定义了一个名为/tmp的卷
* 接着将本地文件系统的一个文件添加进来,并且重命名为“app.jar”,重命名不是必须的,只是一个可用的可选项
* 我们声明想要公开容器的8080端口
* 在容器内运行一个touch命令,这样可以确保app.jar文件的修改日期
* ENTRYPOINT命令定义了容器启动时需要运行的内容——我们运行Java,设置我们的Spring Mongo属性,还有快速的附加属性来加速Tomcat启动时间,然后指向我们的jar包。

现在我们通过运行如下命令构建镜像:
docker build -t microservicedemo/employee . 

我们可以敲入`docker images`看到结果
REPOSITORY                  TAG             IMAGE ID            CREATED             VIRTUAL SIZE
microservicedemo/employee latest 364ffd8162b9 15 minutes ago 846.6 MB


接下来的问题就是“我们的服务容器如何与Mongo容器交互?”,为此,我们引入了容器的link机制。当你运行一个新的容器时,你可以传入一个可选的-link参数指定一个运行中的容器的名字,这样新的容器就可以与这个容器通讯了,所以我们的命令是:

docker run -P -d --name employee --link mongodb microservicedemo/employee


我们启动了一个新的容器,公开了端口(-P),以后台方式运行(-d),命名为employee(—name),接着将这个新的容器连接到了一个名为“mongodb”的容器(-link),连接过程如下:

* 在employee容器的host文件添加一个条目指向MongoDB容器的运行位置
* 在employee容器内添加一些环境变量来协助其他必要的编程访问,可以运行如下命令查看:
docker exec employee bash -c 'env | grep MONGODB'

* 允许容器通过公开的端口来直接通讯,这样就不需要担心主机的部分映射了。如果你还记得上述的内容,我们设置了Spring Mongo到MongoDB的URL作为主机名(mongodb://mongodb/micros),所以有了host文件条目和运行在默认端口的Mongo,Boot应用容器可以连上数据库了

在容器运行时我们可以来执行相同的Web服务,只是这次是作为容器来运行的(对于我来说,容器的8080端口会被映射到虚拟机的32772端口):
467x483xmicroservice-part-3-pic-4.jpg.pagespeed_.ic_.2e11ONwBrl_.jpg

本篇我们已经取得了很大的进展,我们有了两个容器可以工作并且可以互相通讯,接下来的第四篇我们将添加一些额外的服务/容器,可以观察到构建更新的进度并与CI工具集成工作。


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART III(翻译:胡震)

一张图了解Spring Cloud微服务架构

老马 发表了文章 • 0 个评论 • 590 次浏览 • 2019-04-26 20:48 • 来自相关话题

Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置 ...查看全部
Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。Spring Cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A处获取服务。
6295401-8076ec880947ba79.png

Spring Cloud组成的微服务架构图

由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring Cloud中的组件分别充当其中的什么角色。

Fegin(接口调用):微服务之间通过Rest接口通讯,Spring Cloud提供Feign框架来支持Rest的调用,Feign使得不同进程的Rest接口调用得以用优雅的方式进行,这种优雅表现得就像同一个进程调用一样。

Netflix eureka(注册发现):微服务模式下,一个大的Web应用通常都被拆分为很多比较小的Web应用(服务),这个时候就需要有一个地方保存这些服务的相关信息,才能让各个小的应用彼此知道对方,这个时候就需要在注册中心进行注册。每个应用启动时向配置的注册中心注册自己的信息(IP地址,端口号, 服务名称等信息),注册中心将他们保存起来,服务间相互调用的时候,通过服务名称就可以到注册中心找到对应的服务信息,从而进行通讯。注册与发现服务为微服务之间的调用带来了方便,解决了硬编码的问题。服务间只通过对方的服务ID,而无需知道其IP和端口即可以获取对方方服务。

Ribbon(负载均衡):Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon,配置服务提供者的地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者的地址列表,并基于负载均衡算法,请求其中一个服务提供者的实例(为了服务的可靠性,一个微服务可能部署多个实例)。

Hystrix(熔断器):当服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃(雪崩效应)。Hystrix正是为了防止此类问题发生。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

* 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
* 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
* 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
* 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
* 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员指定。

Zuul(微服务网关):不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电影购票的手机APP,可能调用多个微服务的接口才能完成一次购票的业务流程,如果让客户端直接与各个微服务通信,会有以下的问题:

* 客户端会多次请求不同的微服务,增加了客户端的复杂性。
* 存在跨域请求,在一定场景下处理相对复杂。
* 认证复杂,每个服务都需要独立认证。
* 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将很难实施。
* 某些微服务可能使用了对防火墙/浏览器不友好的协议,直接访问时会有一定的困难。

以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。使用微服务网关后,微服务网关将封装应用程序的内部结构,客户端只用跟网关交互,而无须直接调用特定微服务的接口。这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

* 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
* 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
* 减少了客户端与各个微服务之间的交互次数。

Spring Cloud Bus( 统一配置服务):对于传统的单体应用,常使用配置文件管理所有配置。例如一个SpringBoot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。然而,在微服务架构中,微服务的配置管理一般有以下需求:

* 集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。
* 不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。
* 运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
* 配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。综上所述,对于微服务架构而言,一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。Spring Cloud Bus利用Git或SVN等管理配置、采用Kafka或者RabbitMQ等消息总线通知所有应用,从而实现配置的自动更新并且刷新所有微服务实例的配置。

Sleuth+ZipKin(跟踪服务):Sleuth和Zipkin结合使用可以通过图形化的界面查看微服务请求的延迟情况以及各个微服务的依赖情况。需要注意的是Spring Boot 2及以上不在支持Zipkin的自定义,需要到官方网站下载ZipKin相关的jar包。另外需要提一点的是Spring Boot Actuator,提供了很多监控端点如/actuator/info、/actuator/health、/acutator/refresh等,可以查看微服务的信息、健康状况、刷新配置等。

原文链接:一张图了解Spring Cloud微服务架构(作者:SimpleEasy)

如何使用消息队列,Spring Boot和Kubernetes扩展微服务

新牛哥 发表了文章 • 0 个评论 • 1457 次浏览 • 2019-02-10 14:10 • 来自相关话题

【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。 当你设计和构建大规模应用时, ...查看全部
【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。

当你设计和构建大规模应用时,你将面临两个重大挑战:可伸缩性和健壮性

你应该这样设计你的服务,即使它受到间歇性的重负载,它仍能可靠地运行。

以Apple Store为例。

每年都有数百万的Apple客户预先注册购买新的iPhone。

这是数百万人同时购买物品。

如果你要将Apple商店的流量描述为每秒的请求数量,那么它可能是下图的样子:
2.png

现在想象一下,你的任务是构建这样的应用程序。

你正在建立一个商店,用户可以在那里购买自己喜欢的商品。

你构建一个微服务来呈现网页并提供静态资产。 你还构建了一个后端REST API来处理传入的请求。

你希望将两个组件分开,因为这样可以使用相同的REST API,为网站和移动应用程序提供服务。
3.gif

今天是重要的一天,你的商店上线了。

你决定将应用程序扩展为前端四个实例和后端四个实例,因为你预测网站比平常更繁忙。
4.gif

你开始接收越来越多的流量。

前端服务正在很好得处理流量。 但是你注意到连接到数据库的后端正在努力跟上事务的数量。

不用担心,你可以将后端的副本数量扩展到8。
5.gif

你收到的流量更多,后端无法应对。

一些服务开始丢弃连接。 愤怒的客户与你的客服取得联系。 而现在你被淹没在大量流量中。

你的后端无法应付它,它会失去很多连接。
6.gif

你刚丢了一大笔钱,你的顾客也不高兴。

你的应用程序并没有设计得健壮且高可用:

  • 前端和后端紧密耦合——实际上它不能在没有后端的情况下处理应用
  • 前端和后端必须一致扩展——如果没有足够的后端,你可能会淹没在流量中
  • 如果后端不可用,则无法处理传入的事务。
失去事务意味着收入损失。你可以重新设计架构,以便将前端和后端用队列分离。
7.gif
前端将消息发布到队列,而后端则一次处理一个待处理消息。新架构有一些明显的好处:
  • 如果后端不可用,则队列充当缓冲区
  • 如果前端产生的消息多于后端可以处理的消息,则这些消息将缓冲在队列中
  • 你可以独立于前端扩展后端——即你可以拥有数百个前端服务和后端的单个实例
太好了,但是你如何构建这样的应用程序?你如何设计可处理数十万个请求的服务? 你如何部署动态扩展的应用程序?在深入了解部署和扩展的细节之前,让我们关注应用程序。#编写Spring应用程序该服务有三个组件:前端,后端和消息代理。前端是一个简单的Spring Boot Web应用程序,带有Thymeleaf模板引擎。后端是一个消耗队列消息的工作者。由于Spring Boot与JSM能出色得集成,因此你可以使用它来发送和接收异步消息。你可以在learnk8s / spring-boot-k8s-hpa中找到一个连接到JSM的前端和后端应用程序的示例项目。请注意,该应用程序是用Java 10编写的,以利用改进的Docker容器集成能力。只有一个代码库,你可以将项目配置为作为前端或后端运行。你应该知道该应用程序具有:
  • 一个购买物品的主页
  • 管理面板,你可以在其中检查队列中的消息数
  • 一个 `/health` 端点,用于在应用程序准备好接收流量时发出信号
  • 一个 `/submit` 端点,从表单接收提交并在队列中创建消息
  • 一个 `/metrics` 端点,用于公开队列中待处理消息的数量(稍后将详细介绍)
该应用程序可以在两种模式下运行:作为前端,应用程序呈现人们可以购买物品的网页。
8.png
作为工作者,应用程序等待队列中的消息并处理它们。
9.png
请注意,在示例项目中,使用`Thread.sleep(5000)`等待五秒钟来模拟处理。你可以通过更改`application.yaml`中的值来在任一模式下配置应用程序。#模拟应用程序的运行默认情况下,应用程序作为前端和工作程序启动。你可以运行该应用程序,只要你在本地运行ActiveMQ实例,你就应该能够购买物品并让系统处理这些物品。
10.gif
如果检查日志,则应该看到工作程序处理项目。它确实工作了!编写Spring Boot应用程序很容易。一个更有趣的主题是学习如何将Spring Boot连接到消息代理。#使用JMS发送和接收消息Spring JMS(Java消息服务)是一种使用标准协议发送和接收消息的强大机制。如果你以前使用过JDBC API,那么你应该熟悉JMS API,因为它的工作方式很类似。你可以按JMS方式来使用的最流行的消息代理是ActiveMQ——一个开源消息服务器。使用这两个组件,你可以使用熟悉的接口(JMS)将消息发布到队列(ActiveMQ),并使用相同的接口来接收消息。更妙的是,Spring Boot与JMS的集成非常好,因此你可以立即加快速度。实际上,以下短类封装了用于与队列交互的逻辑:
@Componentpublic 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());    }  }} 
你可以使用`send`方法将消息发布到命名队列。此外,Spring Boot将为每个传入消息执行`onMessage`方法。最后一个难题是指示Spring Boot使用该类。你可以通过在Spring Boot应用程序中注册侦听器来在后台处理消息,如下所示:
@SpringBootApplication@EnableJmspublic 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);  }} 
其中id是使用者的唯一标识符,destination是队列的名称。你可以从GitHub上的项目中完整地读取Spring队列服务的源代码。回顾一下你是如何在少于40行代码中编写可靠队列的。你一定很喜欢Spring Boot。# 你在部署时节省的所有时间都可以专注于编码你验证了应用程序的工作原理,现在是时候部署它了。此时,你可以启动VPS,安装Tomcat,并花些时间制作自定义脚本来测试,构建,打包和部署应用程序。或者你可以编写你希望拥有的描述:一个消息代理和两个使用负载均衡器部署的应用程序。诸如Kubernetes之类的编排器可以阅读你的愿望清单并提供正确的基础设施。由于花在基础架构上的时间减少意味着更多的时间编码,这次你将把应用程序部署到Kubernetes。但在开始之前,你需要一个Kubernetes集群。你可以注册Google云平台或Azure,并使用Kubernetes提供的云提供商服务。或者,你可以在将应用程序移动到云上之前在本地尝试Kubernetes。`minikube`是一个打包为虚拟机的本地Kubernetes集群。如果你使用的是Windows,Linux和Mac,那就太好了,因为创建群集需要五分钟。你还应该安装`kubectl`,即连接到你的群集的客户端。你可以从官方文档中找到有关如何安装`minikube`和`kubectl`的说明。

如果你在Windows上运行,则应查看有关如何安装Kubernetes和Docker的详细指南

你应该启动一个具有8GB RAM和一些额外配置的集群:
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`实例,则可以通过销毁VM来重新调整VM的大小。 只需添加`--memory 8096`就不会有任何影响。验证安装是否成功。 你应该看到以列表形式展示的一些资源。 集群已经准备就绪,也许你应该立即开始部署?还不行。你必须先装好你的东西。# 什么比uber-jar更好?容器部署到Kubernetes的应用程序必须打包为容器。毕竟,Kubernetes是一个容器编排器,所以它本身无法运行你的jar。容器类似于fat jar:它们包含运行应用程序所需的所有依赖项。甚至JVM也是容器的一部分。所以他们在技术上是一个更胖的fat-jar。将应用程序打包为容器的流行技术是Docker。虽然Docker是最受欢迎的,但它并不是唯一能够运行容器的技术。其他受欢迎的选项包括`rkt`和`lxd`。如果你没有安装Docker,可以按照Docker官方网站上的说明进行操作。通常,你构建容器并将它们推送到仓库。它类似于向Artifactory或Nexus推送jar包。但在这种特殊情况下,你将在本地工作并跳过仓库部分。实际上,你将直接在`minikube`中创建容器镜像。首先,按照此命令打印的说明将Docker客户端连接到`minikube`:
minikube docker-env
请注意,如果切换终端,则需要重新连接`minikube`内的Docker守护程序。 每次使用不同的终端时都应遵循相同的说明。并从项目的根目录构建容器镜像:
docker build -t spring-k8s-hp0a .
你可以验证镜像是否已构建并准备好运行:
docker images |grep spring 
很好。群集已准备好,你打包应用程序,也许你已准备好立即部署?是的,你最终可以要求Kubernetes部署应用程序。# 将你的应用程序部署到Kubernetes你的应用程序有三个组件:
  • 呈现前端的Spring Boot应用程序
  • ActiveMQ作为消息代理
  • 处理事务的Spring Boot后端
你应该分别部署这三个组件。对于每个组件你都应该创建:
  • Deployment对象,描述部署的容器及其配置
  • 一个Service对象,充当Deployment部署创建的应用程序的所有实例的负载均衡器
部署中的每个应用程序实例都称为Pod
11.gif
# 部署ActiveMQ让我们从ActiveMQ开始吧。你应该创建一个`activemq-deployment.yaml`文件,其中包含以下内容:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: queuespec:  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的内存
  • 你要求提供单个副本 - 你的应用程序的单个实例
使用以下内容创建`activemq-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: queuespec:  ports:  - port: 61616     targetPort: 61616  selector:    app: queue
幸运的是,这个模板更短!这个yaml表示:
  • 你创建了一个公开端口61616的负载均衡器
  • 传入流量分发到所有具有`app:queue`类型标签的Pod(请参阅上面的部署)
  • `targetPort`是Pod暴露的端口
你可以使用以下命令创建资源:
kubectl create -f activemq-deployment.yamlkubectl create -f activemq-service.yaml
你可以使用以下命令验证数据库的一个实例是否正在运行:
kubectl get pods -l=app=queue
# 部署前端使用以下内容创建`fe-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: frontendspec:  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
Deployment 看起来很像前一个。但是有一些新的字段:
  • 有一个section可以注入环境变量
  • 还有Liveness探针,可以告诉你应用程序何时可以接受流量
使用以下内容创建`fe-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: frontendspec:  ports:  - nodePort: 32000    port: 80    targetPort: 8080  selector:    app: frontend  type: NodePort
你可以使用以下命令创建资源:
kubectl create -f fe-deployment.yamlkubectl create -f fe-service.yaml
你可以使用以下命令验证前端应用程序的一个实例是否正在运行:
kubectl get pods -l=app=frontend
# 部署后端使用以下内容创建`backend-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: backendspec:  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
使用以下内容创建`backend-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: backend  spec:    ports:    - nodePort: 31000      port: 80      targetPort: 8080    selector:      app: backend    type: NodePort
你可以使用以下命令创建资源:
kubectl create -f backend-deployment.yamlkubectl create -f backend-service.yaml
你可以验证后端的一个实例是否正在运行:
kubectl get pods -l=app=backend
部署完成。它真的有效吗?你可以使用以下命令在浏览器中访问该应用程序:
minikube service backend
minikube service frontend
如果它有效,你应该尝试购买一些物品!工作者在处理交易吗?是的,如果有足够的时间,工作人员将处理所有待处理的消息。恭喜!你刚刚将应用程序部署到Kubernetes!# 手动扩展以满足不断增长的需求单个工作程序可能无法处理大量消息。 实际上,它当时只能处理一条消息。如果你决定购买数千件物品,则需要数小时才能清除队列。此时你有两个选择:
  • 你可以手动放大和缩小
  • 你可以创建自动缩放规则以自动向上或向下扩展
让我们先从基础知识开始。你可以使用以下方法将后端扩展为三个实例:
kubectl scale --replicas=5 deployment/backend
你可以验证Kubernetes是否创建了另外五个实例:
kubectl get pods
并且应用程序可以处理五倍以上的消息。一旦工人排空队列,你可以缩小:
kubectl scale --replicas=1 deployment/backend
如果你知道最多的流量何时达到你的服务,手动扩大和缩小都很棒。如果不这样做,设置自动缩放器允许应用程序自动缩放而无需手动干预。你只需要定义一些规则。# 公开应用程序指标Kubernetes如何知道何时扩展你的申请?很简单,你必须告诉它。自动调节器通过监控指标来工作。 只有这样,它才能增加或减少应用程序的实例。因此,你可以将队列长度公开为度量标准,并要求autoscaler观察该值。 队列中的待处理消息越多,Kubernetes将创建的应用程序实例就越多。那么你如何公开这些指标呢?应用程序具有 `/metrics` 端点以显示队列中的消息数。 如果你尝试访问该页面,你会注意到以下内容:
# HELP messages Number of messages in the queue # TYPE messages gaugemessages 0
应用程序不会将指标公开为JSON格式。 格式为纯文本,是公开Prometheus指标的标准。 不要担心记忆格式。 大多数情况下,你将使用其中一个Prometheus客户端库。# 在Kubernetes中使用应用程序指标你几乎已准备好进行自动缩放——但你应首先安装度量服务器。 实际上,默认情况下,Kubernetes不会从你的应用程序中提取指标。 如果你愿意,可以启用Custom Metrics API。要安装Custom Metrics API,你还需要Prometheus - 时间序列数据库。 安装Custom Metrics API所需的所有文件都可以方便地打包在learnk8s / spring-boot-k8s-hpa中。你应下载该存储库的内容,并将当前目录更改为该项目的`monitoring`文件夹。
cd spring-boot-k8s-hpa/monitoring
从那里,你可以创建自定义指标API:
kubectl create -f ./metrics-serverkubectl create -f ./namespaces.yamlkubectl create -f ./prometheuskubectl 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 .
恭喜,你有一个公开指标的应用程序和使用它们的指标服务器。你最终可以启用自动缩放器!# 在Kubernetes中进行自动扩展部署Kubernetes有一个名为Horizontal Pod Autoscaler的对象,用于监视部署并上下调整Pod的数量。你将需要其中一个来自动扩展实例。你应该创建一个包含以下内容的`hpa.yaml`文件:
apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata:  name: spring-boot-hpaspec:  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
自动定标器表示它能够将Pod扩展到2,并且它已准备好监视部署。令人兴奋的东西,但它有效吗?# 负载测试只有一种方法可以知道它是否有效:在队列中创建大量消息。转到前端应用程序并开始添加大量消息。 在添加消息时,使用以下方法监视Horizontal Pod Autoscaler的状态:
kubectl describe hpa
Pod的数量从2上升到4,然后是8,最后是10。该应用程序随消息数量而变化! 欢呼!你刚刚部署了一个完全可伸缩的应用程序,可根据队列中的待处理消息数进行扩展。另外,缩放算法如下:
MAX(CURRENT_REPLICAS_LENGTH * 2, 4)
在解释算法时,文档没有多大帮助。 你可以在代码中找到详细信息。此外,每分钟都会重新评估每个放大,而每两分钟缩小一次。以上所有都是可以调整的设置。但是你还没有完成。# 什么比自动缩放实例更好? 自动缩放群集。跨节点缩放Pod非常有效。 但是,如果群集中没有足够的容量来扩展Pod,该怎么办?如果达到峰值容量,Kubernetes将使Pods处于暂挂状态并等待更多资源可用。如果你可以使用类似于Horizontal Pod Autoscaler的自动缩放器,但对于节点则会很棒。好消息!你可以拥有一个群集自动缩放器,可以在你需要更多资源时为Kubernetes群集添加更多节点。
12.gif
群集自动缩放器具有不同的形状和大小。它也是特定于云提供商的。请注意,你将无法使用`minikube`测试自动缩放器,因为它根据定义是单节点。你可以在Github上找到有关集群自动调节器和云提供程序实现的更多信息。# 概览设计大规模应用程序需要仔细规划和测试。基于队列的体系结构是一种出色的设计模式,可以解耦你的微服务并确保它们可以独立扩展和部署。虽然你可以用脚本来扩展你的应用,但可以更轻松地利用容器编排器(如Kubernetes)自动部署和扩展应用程序。# 今天先说到这里感谢Nathan Cashmore和Andy Griffiths的反馈!如果你喜欢这篇文章,下面的文章可能你也会有兴趣:
  • 3个简单的技巧,用于较小的Docker镜像,并学习如何更快地构建和部署Docker镜像。
  • Kubernetes Chaos Engineering:经验教训 - 第1部分Kubernetes出现问题时会发生什么? Kubernetes可以从失败和自我愈合中恢复吗?
#成为Kubernetes中部署和扩展应用程序的专家学习如何使用Horizo​​ntal Pod Autoscaler部署和扩展应用程序只是一个开始!从我们的实践课程开始,学习如何掌握Kubernetes。了解如何:
  • 不费工夫得处理最繁忙的流量网站
  • 将你的工作扩展到数千台服务器,并将等待时间从几天缩短到几分钟
  • 知道你的应用程序具有多云设置的高可用性,让你高枕无忧
  • 只需使用你需要的资源,就可以节省大量现金
  • 为你的交付管道增压并全天候部署应用程序

成为Kubernetes的专家

原文链接:How to scale Microservices with Message Queues, Spring Boot, and Kubernetes (翻译:池剑锋)

使用Spring Cloud和Docker构建微服务架构

李颖杰 发表了文章 • 0 个评论 • 20841 次浏览 • 2017-10-07 21:49 • 来自相关话题

【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式 ...查看全部
【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式的起点。

该代码可以在GitHub上获得,并且在Docker Hub上提供了镜像。您只需要一个命令即可启动整个系统。

我选择了一个老项目作为这个系统的基础,它的后端以前是单一应用。此应用提供了处理个人财务、整理收入开销、管理储蓄、分析统计和创建简单预测等功能。
#功能服务
整个应用分解为三个核心微服务。它们都是可以独立部署的应用,围绕着某些业务功能进行组织。
1.png


账户服务

包含一般用户输入逻辑和验证:收入/开销记录、储蓄和账户设置。
biao1.png


统计服务

计算主要的统计参数,并捕获每一个账户的时间序列。数据点包含基于货币和时间段正常化后的值。该数据可用于跟踪账户生命周期中的现金流量动态。
biao2.png


通知服务

存储用户的联系信息和通知设置(如提醒和备份频率)。安排工作人员从其它服务收集所需的信息并向订阅的客户发送电子邮件。
biao3.png


注意

* 每一个微服务拥有自己的数据库,因此没有办法绕过API直接访问持久数据。
* 在这个项目中,我使用MongoDB作为每一个服务的主数据库。拥有一个多种类持久化架构(polyglot persistence architecture)也是很有意义的。
* 服务间(Service-to-service)通信是非常简单的:微服务仅使用同步的REST API进行通信。现实中的系统的常见做法是使用互动风格的组合。例如,执行同步的GET请求检索数据,并通过消息代理(broker)使用异步方法执行创建/更新操作,以便解除服务和缓冲消息之间的耦合。然而,这带给我们是最终的一致性

#基础设施服务
分布式系统中常见的模式,可以帮助我们描述核心服务是怎样工作的。Spring Cloud提供了强大的工具,可以增强Spring Boot应用的行为来实现这些模式。我会简要介绍一下:
2.png

##配置服务
Spring Cloud Config是分布式系统的水平扩展集中式配置服务。它使用了当前支持的本地存储、Git和Subversion等可拔插存储库层(repository layer)。

在此项目中,我使用了native profile,它简单地从本地classpath下加载配置文件。您可以在配置服务资源中查看shared目录。现在,当通知服务请求它的配置时,配置服务将响应回shared/notification-service.yml和shared/application.yml(所有客户端应用之间共享)。

客户端使用

只需要使用sprng-cloud-starter-config依赖构建Spring Boot应用,自动配置将会完成其它工作。

现在您的应用中不需要任何嵌入的properties,只需要提供有应用名称和配置服务url的bootstrap.yml即可:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true

使用Spring Cloud Config,您可以动态更改应用配置

比如,EmailService bean使用了@RefreshScope注解。这意味着您可以更改电子邮件的内容和主题,而无需重新构建和重启通知服务应用。

首先,在配置服务器中更改必要的属性。然后,对通知服务执行刷新请求:curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh。

您也可以使用webhook来自动执行此过程

注意

* 动态刷新存在一些限制。@RefreshScope不能和@Configuraion类一同工作,并且不会作用于@Scheduled方法。
* fail-fast属性意味着如果Spring Boot应用无法连接到配置服务,将会立即启动失败。当您一起启动所有应用时,这非常有用。
* 下面有重要的安全提示

##授权服务
负责授权的部分被完全提取到单独的服务器,它为后端资源服务提供OAuth2令牌。授权服务器用于用户授权以及在周边内进行安全的机器间通信。

在此项目中,我使用密码凭据作为用户授权的授权类型(因为它仅由本地应用UI使用)和客户端凭据作为微服务授权的授权类型。

Spring Cloud Security提供了方便的注解和自动配置,使其在服务器端或者客户端都可以很容易地实现。您可以在文档中了解到更多信息,并在授权服务器代码中检查配置明细。

从客户端来看,一切都与传统的基于会话的授权完全相同。您可以从请求中检索Principal对象、检查用户角色和其它基于表达式访问控制和@PreAuthorize注解的内容。

PiggyMetrics(帐户服务、统计服务、通知服务和浏览器)中的每一个客户端都有一个范围:用于后台服务的服务器、用于浏览器展示的UI。所以我们也可以保护控制器避免受到外部访问,例如:
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}

##API网关
您可以看到,有三个核心服务。它们向客户端暴露外部API。在现实系统中,这个数量可以非常快速地增长,同时整个系统将变得非常复杂。实际上,一个复杂页面的渲染可能涉及到数百个服务。

理论上,客户端可以直接向每个微服务直接发送请求。但是这种方式是存在挑战和限制的,如果需要知道所有端点的地址,分别对每一段信息执行http请求,将结果合并到客户端。另一个问题是,这不是web友好协议,可能只在后端使用。

通常一个更好的方法是使用API网关。它是系统的单个入口点,用于通过将请求路由到适当的后端服务或者通过调用多个后端服务并聚合结果来处理请求。此外,它还可以用于认证、insights、压力测试、金丝雀测试(canary testing)、服务迁移、静态响应处理和主动变换管理。

Netflix开源这样的边缘服务,现在用Spring Cloud,我们可以用一个@EnabledZuulProxy注解来启用它。在这个项目中,我使用Zuul存储静态内容(UI应用),并将请求路由到适当的微服务。以下是一个简单的基于前缀(prefix-based)路由的通知服务配置:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false

这意味着所有以/notification开头的请求将被路由到通知服务。您可以看到,里面没有硬编码的地址。Zuul使用服务发现机制来定位通知服务实例以及断路器和负载均衡器,如下所述。
##服务发现
另一种常见的架构模式是服务发现。它允许自动检测服务实例的网络位置,由于自动扩展、故障和升级,它可能会动态分配地址。

服务发现的关键部分是注册。我使用Netflix Eureka进行这个项目,当客户端需要负责确定可以用的服务实例(使用注册服务器)的位置和跨平台的负载均衡请求时,Eureka就是客户端发现模式的一个很好的例子。

使用Spring Boot,您可以使用spring-cloud-starter-eureka-server依赖、@EnabledEurekaServer注解和简单的配置属性轻松构建Eureka注册中心(Eureka Registry)。

使用@EnabledDiscoveryClient注解和带有应用名称的bootstrap.yml来启用客户端支持:
spring:
application:
name: notification-service

现在,在应用启动时,它将向Eureka服务器注册并提供元数据,如主机和端口、健康指示器URL、主页等。Eureka接收来自从属于某服务的每个实例的心跳消息。如果心跳失败超过配置的时间表,该实例将从注册表中删除。

此外,Eureka还提供了一个简单的界面,您可以通过它来跟踪运行中的服务和可用实例的数量:http://localhost:8761
3.png

##负载均衡器、断路器和Http客户端
Netflix OSS提供了另一套很棒的工具。

Ribbon

Ribbon是一个客户端负载均衡器,可以很好地控制HTTP和TCP客户端的行为。与传统的负载均衡器相比,每次线上调用都不需要额外的跳跃——您可以直接联系所需的服务。

它与Spring Cloud和服务发现是集成在一起的,可开箱即用。Eureka客户端提供了可用服务器的动态列表,因此Ribbon可以在它们之间进行平衡。

Hystrix

Hystrix是断路器模式的一种实现,它可以通过网络访问依赖来控制延迟和故障。中心思想是在具有大量微服务的分布式环境中停止级联故障。这有助于快速失败并尽快恢复——自我修复在容错系统中是非常重要的。

除了断路器控制,在使用Hystrix,您可以添加一个备用方法,在主命令失败的情况下,该方法将被调用以获取默认值。

此外,Hystrix生成每个命令的执行结果和延迟的度量,我们可以用它来监视系统的行为

Feign

Feign是一个声明式HTTP客户端,能与Ribbon和Hystrix无缝集成。实际上,通过一个spring-cloud-starter-feign依赖和@EnabledFeignClients注解,您可以使用一整套负载均衡器、断路器和HTTP客户端,并附带一个合理的的默认配置。

以下是账户服务的示例:
@FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}


* 您需要的只是一个接口
* 您可以在Spring MVC控制器和Feign方法之间共享@RequestMapping部分
* 以上示例仅指定所需要的服务ID——statistics-service,这得益于Eureka的自动发现(但显然您可以使用特定的URL访问任何资源)。

##监控仪表盘
在这个项目配置中,Hystrix的每一个微服务都通过Spring Cloud Bus(通过AMQP broker)将指标推送到Turbine。监控项目只是一个使用了TurbineHystrix仪表盘的小型Spring Boot应用。

让我们看看系统行为在负载下:账户服务调用统计服务和它在一个变化的模拟延迟下的响应。响应超时阈值设置为1秒。
4.png

##日志分析
集中式日志记录在尝试查找分布式环境中的问题时非常有用。Elasticsearch、Logstash和Kibana技术栈可让您轻松搜索和分析您的日志、利用率和网络活动数据。在我的另一个项目中已经有现成的Docker配置。
##安全

高级安全配置已经超过了此概念性项目的范围。为了更真实地模拟真实系统,请考虑使用https和JCE密钥库来加密微服务密码和配置服务器的properties内容(有关详细信息,请参阅文档)。
#基础设施自动化

部署微服务比部署单一的应用的流程要复杂得多,因为它们相互依赖。拥有完全基础设置自动化是非常重要的。我们可以通过持续交付的方式获得以下好处:

* 随时发布软件的能力。
* 任何构建都可能最终成为一个发行版本。
* 构建工件(artifact)一次,根据需要进行部署。

这是一个简单的持续交付工作流程,在这个项目的实现:

在此配置中,Travis CI为每一个成功的Git推送创建了标记镜像。因此,每一个微服务在Docker Hub上的都会有一个latest镜像,而较旧的镜像则使用Git提交的哈希进行标记。如果有需要,可以轻松部署任何一个,并快速回滚。
5.png

#如何运行全部?
这真的很简单,我建议您尝试一下。请记住,您将要启动8个Spring Boot应用、4个MongoDB实例和RabbitMq。确保您的机器上有4GB的内存。您可以随时通过网关、注册中心、配置、认证服务和账户中心运行重要的服务。

运行之前

* 安装Docker和Docker Compose。
* 配置环境变量:CONFIG_SERVICE_PASSWORD, NOTIFICATION_SERVICE_PASSWORD, STATISTICS_SERVICE_PASSWORD, ACCOUNT_SERVICE_PASSWORD, MONGODB_PASSWORD

生产模式

在这种模式下,所有最新的镜像都将从Docker Hub上拉取。只需要复制docker-compose.yml并执行docker-compose up -d即可。

开发模式

如果您想自己构建镜像(例如,在代码中进行一些修改),您需要克隆所有仓库(repository)并使用Mavne构建工件(artifact)。然后,运行docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

docker-compose.dev.yml继承了docker-compose.yml,附带额外配置,可在本地构建镜像,并暴露所有容器端口以方便开发。

重要的端点(Endpoint)

* localhost:80 —— 网关
* localhost:8761 —— Eureka仪表盘
* localhost:9000 —— Hystrix仪表盘
* localhost:8989 —— Turbine stream(Hystrix仪表盘来源)
* localhost:15672 —— RabbitMq管理

注意

所有Spring Boot应用都需要运行配置服务器才能启动。得益于Spring Boot的fail-fast属性和docker-compsoe的restart:always选项,我们可以同时启动所有容器。这意味着所有依赖的容器将尝试重新启动,直到配置服务器启动运行为止。

此外,服务发现机制在所有应用启动后需要一段时间。在实例、Eureka服务器和客户端在其本地缓存中都具有相同的元数据之前,任何服务都不可用于客户端发现,因此可能需要3次心跳。默认的心跳周期为30秒。

原文链接:Microservice Architectures With Spring Cloud and Docker(翻译:Oopsguy

Spring Cloud在国内中小型公司能用起来吗?

尼古拉斯 发表了文章 • 0 个评论 • 2958 次浏览 • 2017-09-12 19:25 • 来自相关话题

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring B ...查看全部
今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring Boot和Spring Cloud近两年的程序员,看的我手痒痒不答不快呀。
#好问题
好问题必须配认真的回答,仔细的看了题主的问题,发现这个问题非常具有代表性,可能是广大网友想使用Spring Cloud却又对Spring Cloud不太了解的共同想法,题主对Spring Cloud使用的方方面面都进行过了思考,包括市场,学习、前后端、测试、配置、部署、开发以及运维,下面就是题主原本的问题:

想在公司推广Spring Cloud,但我对这项技术还缺乏了解,画了一张脑图,总结了种种问题。

01.jpg

微服务是这样一个结构吗:



前端或二方 - > ng集群 -> zuul集群 -> eureka-server集群 -> service provider集群
(二方指其他业务部门)



想要明白这个问题,首先需要知道什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系?
#什么是Spring Boot?
Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只需要很少的Spring配置。

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架(不知道这样比喻是否合适)。

Spring Boot的核心思想就是约定大于配置,一切自动完成。采用Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。如果你对Spring Boot完全不了解,可以参考我的这篇文章:《SpringBoot(一):入门篇-%E5%85%A5%E9%97%A8%E7%AF%87.html)》。
#什么是Spring Cloud?
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud做为大管家就需要提供各种方案来维护整个生态。

Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能,如果你对Spring Cloud组件不是特别了解的话,可以参考我的这篇文章:《Spring Cloud(一):大话Spring Cloud》。
#Spring Boot和Spring Cloud的关系
Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。

Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

Spring -> Spring Boot > Spring Cloud 这样的关系。
#回答

以下为我在知乎的回答。

首先楼主问的这些问题都挺好的,算是经过了自己的一番思考,我恰好经历了你所说的中小公司,且都使用Spring Cloud并且已经投产上线。第一家公司技术开发人员15人左右,项目实例 30多,第二家公司开发人员100人左右,项目实例达160多。

实话说Spring Boot、Spring Cloud仍在高速发展,技术生态不断的完善和扩张,不免也会有一些小的bug,但对于中小公司的使用来将,完全可以忽略,基本都可以找到解决方案,接下来回到你的问题。
##1、市场
据我所知有很多知名互联网公司都已经使用了Spring Cloud,比如阿里、美团但都是小规模,没有像我经历的这俩家公司,业务线全部拥抱Spring Cloud;另外Spring Cloud并不是一套高深的技术,普通的Java程序员经过一到俩个月完全就可以上手,但前期需要一个比较精通人的来带队。
##2、学习
有很多种方式,现在Spring Cloud越来越火的情况下,各种资源也越来越丰富,查看官方文档和示例,现在很多优秀的博客在写spirng cloud的相关教程,我这里收集了一些Spring Boot和Spring Cloud的相关资源可以参考,找到博客也就找到人和组织了。
##3、前后职责划分
其实这个问题是每个系统架构都应该考虑的问题,Spring Cloud只是后端服务治理的一套框架,唯一和前端有关系的是thymeleaf,Spring推荐使用它做模板引擎。一般情况下,前端App或者网页通过Zuul来调用后端的服务,如果包含静态资源也可以使用Nginx做一下代理转发。
##4、测试
Spring-boot-starter-test支持项目中各层方法的测试,也支持controller层的各种属性。所以一般测试的步奏是这样,首先开发人员覆盖自己的所有方法,然后测试微服务内所有对外接口保证微服务内的正确性,再进行微服务之间集成测试,最后交付测试。
##5、配置
session共享有很多种方式,比如使用tomcat sesion共享机制,但我比较推荐使用Redis缓存来做session共享。完全可以分批引入,我在上一家公司就是分批过渡上线,新旧项目通过Zuul进行交互,分批引入的时候,最好是新业务线先使用Spring Cloud,老业务做过渡,当完全掌握之后在全部替换。如果只是请求转发,Zuul的性能不一定比Nginx低,但是如果涉及到静态资源,还是建议在前端使用Nginx做一下代理。另外Spring Cloud有配置中心,可以非常灵活的做所有配置的事情。
##6、部署
多环境不同配置,Spring Boot最擅长做这个事情了,使用不同的配置文件来配置不同环境的参数,在服务启动的时候指明某个配置文件即可,例如:java -jar app.jar --spring.profiles.active=dev就是启动测试环境的配置文件;Spring Cloud 没有提供发布平台,因为Jenkins已经足够完善了,推荐使用jenkins来部署Spring Boot项目,会省非常多的事情;灰度暂时不支持,可能需要自己来做,如果有多个实例,可以一个一个来更新;支持混合部署,一台机子部署多个是常见的事情。
##7、开发
你说的包含html接口就是前端页面吧,Spring Boot可以支持,但其实也是Spring Mvc在做这个事情,Spring Cloud只做服务治理,其它具体的功能都是集成了各种框架来解决而已;excel报表可以,其实除过swing项目外,其它Java项目都可以想象;Spring Cloud和老项目可以混合使用,通过Zuul来支持。是否支持callback,可以通过MQ来实现,还是强调Spring Cloud只是服务治理。
##8、运维
Turbine、Zipkin可以用来做熔断和性能监控;动态上下线某个节点可以通过Jenkins来实现;Provider下线后,会有其它相同的实例来提供服务,Eureka会间隔一段时间来检测服务的可用性;不同节点配置不同的流量权值目前还不支持。注册中心必须做高可用集群,注册中心挂掉之后,服务实例会全部停止。

总结,中小企业是否能用的起来Spring Cloud,完全取决于自己公司的环境,如果是一个技术活跃型的团队就大胆的去尝试吧,目前Spring Cloud是所有微服务治理中最优秀的方案,也是一个趋势,未来一两年可能就会像Spring一样流行,早接触早学习岂不更好。

希望能解答了你的疑问。
#Spring Cloud 架构
我们从整体来看一下Spring Cloud主要的组件,以及它的访问流程:
02.jpg


* 外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务
* 网关接收到请求后,从注册中心(Eureka)获取可用服务
* 由Ribbon进行均衡负载后,分发到后端的具体实例
* 微服务之间通过Feign进行通信处理业务
* Hystrix负责处理服务超时熔断
* Turbine监控服务间的调用和熔断相关指标

图中没有画出配置中心,配置中心管理各微服务不同环境下的配置文件。

以上就是一个完整的Spring Cloud生态图。

最后送一个完整示例的Spirng Cloud开源项目等你去拿:https://github.com/ityouknow/spring-cloud-examples

原文链接:https://juejin.im/post/59b72f865188257e8153732d

Spring Boot与Docker(四):额外的微服务、更新容器、Docker Compose和负载均衡

国会山上的猫TuxHu 发表了文章 • 0 个评论 • 7227 次浏览 • 2015-12-15 23:11 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adba ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治●华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

现在我们对于微服务和Docker有了扎实的了解,启动了一个MongoDB容器和Spring Boot微服务容器并且借助于容器的link机制(参考我们的Git版本库第四部分开始部分)实现了它们之间的相互通讯。为了完成我们最初的用例,我们需要两个微服务——分别是“missions”和“rewards”。我将开始这个话题并按照与我们之前构建employee微服务相同的方式来构建这两个微服务,可以参考Git版本库第四部分的第一步来获得这两个微服务容器。现在如果我们执行docker ps,我们将看到如下的信息,其中的一些列为了简洁被移掉了:
CONTAINER ID IMAGE                      PORTS                         NAMES
86bd9bc19917 microservicedemo/employee 0.0.0.0:32779->8080/tcp employee
1c694e248c0a microservicedemo/reward 0.0.0.0:32775->8080/tcp reward
c3b5c56ff3f9 microservicedemo/mission 0.0.0.0:32774->8080/tcp mission
48647d735188 mongo 0.0.0.0:32771->27017/tcp mongodb


更新镜像
这都是很简单的,但是没有太大的作用,因为此时没有一个微服务可以在简单的数据CRUD功能之外带来任何直接的价值。让我们开始对一些代码的更改进行分层,以提供更多的价值和功能。我们会对某个微服务做出一些改变,然后处理如何更新镜像,了解对于容器的版本控制。由于员工通过完成任务来获得积分,我们需要追踪他们的任务完成情况、积分总计(获得和活跃的)以及奖励兑换。我们将添加一些额外的类到Employee模型中——这些不是顶层的业务对象,所以它们不会有它们自己的微服务,但是将会在Employee对象中提供上下文内容。一旦这些变化做出了(参见Git版本库第四部分第二步),将会有一些结构性的改变,需要在整个软件栈中同步,更新镜像的步骤如下:
● 重新编译源代码
gradle build

● 重新构建镜像
docker build -t microservicedemo/employee .

最后将会看到一些如下的消息:
Removing intermediate container 5ca297c19885 Successfully build 088558247

● 现在我们需要删除旧容器,替换为新的:
docker stop employee
docker rm employee
docker run -P -d --name employee --link mongodb microservicedemo/employee


需要注意的重要事项就是在运行容器内的代码是不会更新的,容器和微服务的另外一个核心原则是容器内部的代码和配置是不可变的。换句话说,你不用更新容器,只需要替换它。这对于一些容器的使用案例会造成一些问题,比如使用容器来操作数据库或者其它的持久化资源。

使用Docker Compose来编排容器
像我一样,如果你在本系列文章之间有其他的工作要做,如何确保所有的各种命令行参数都可以来连接这些容器,可能有点令人沮丧。编排这一队容器就是Docker Compose(以前被成为Fig)的目的。在Yaml配置文件中定义你的一组容器,并且管理这些容器的运行时配置。在很多方面,可以把Docker Compose想象成为一个编排者,确保“正运行”的容器有着正确的选项和配置。我们将通过命令行参数为我们的应用创建一个这样的编排者来做所有管理想做的事情。
docker-compose.yml:
employee:
build: employee
ports:
- "8080"
links:
- mongodb
reward:
build: reward
ports:
- "8080"
links:
- mongodb
mission:
build: mission
ports:
- "8080"
links:
- mongodb
mongodb:
image: mongo


接着在命令行上敲入:
docker-compose up -d


然后整个一队容器都将会启动,非常方便!许多Docker命令在Docker-Compose上都有类似的命令,如果我们运行"`docker-compose ps`",我们会看到:

Name Command State Ports
-------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp


容器的动态伸缩和负载均衡
不过上述这些还不是Docker Compose可以做的所有工作,如果你运行“`docker-compose scale [compose container name]=3`”,,将会创建多个容器实例。比如运行“`docker-compose scale employee=3`”,接着运行“`docker-compose ps`”,将会看到:

Name Command State Ports
---------------------------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_employee_2 java -Dspring.data.mongodb ... Up 0.0.0.0:32791->8080/tcp
git_employee_3 java -Dspring.data.mongodb ... Up 0.0.0.0:32790->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp

我们的employee容器现在有了三个实例!Docker Compse记得你设置的数量,所以下次运行的时候,将会启动三个employee容器实例,就我个人而言,我认为这个应该在docker-compose.yml文件中设置,但是其实不是的。

希望你开始看到一个问题如何发展的。我们应该如何构建一个针对终端用户的事实上使用微服务的应用呢?因为容器的端口变了,并且在一个Docker集群服务器环境中(比如Docker Swarm),宿主机的IP地址也会改变。有一些先进的解决方案(Kubernetes和AWS的ECS),但是现在我们将寻找一个相对简单的选项,将采用一个非常容易的方法来对容器实例做负载均衡。Tutum是一家构造多重云容器组织能力的公司,已经给Docker社区提交了一个HAProxy的扩展插件,这个扩展插件会基于连接的容器自动配置其自身。让我们为多个employee容器实例添加一个负载均衡器,我们将其添加到docker-compose.yml文件中:

ha_employee:
image: tutum/haproxy
links:
- employee
ports:
- "8080:80"


接着我们运行“`docker-compose up -d`”,将会下载缺失的镜像并且启动容器。现在我们可以再次在特定的端口(8080)运行测试,这次将会对于所有运行的employee容器进行负载均衡。接着,我们可以在192.168.99.100:8080上敲击employee服务集群,默认情况下将会轮循这三个实例。易如反掌!HAProxy的Docker容器还有许多额外的特性和功能点,我建议可以从https://github.com/tutumcloud/haproxy获得更多的信息。

HAProxy对于一个特定容器的多个实例的负载均衡处理地非常巧妙,是单容器环境的理想选择。然而,我们没有这样的环境,那咋办?我们可以启动多个HAProxy实例来处理容器集群,在宿主机的不同端口上转发每一个HAProxy容器端口,所以我们的employee服务放在了8080端口,mission服务放在8081端口,reward服务放在8082端口(参考Git版本库第四部分第三步)。如果我们来到生产环境,我们可以利用Nginx来创建反向代理,将所有的服务请求转发到一个单独的IP地址和端口上(通过URL路径/employee/和/reward/路由到相应的容器)。或者我们可以使用一个更加健壮的服务发现路由,比如这儿的利用etcd和一些让人印象深刻的Docker元数据脚本和模板引擎,来自于Jason Wilder的Docker-gen系统(https://hub.docker.com/r/jwilder/docker-gen/),以及大量的额外的自我管理的服务发现的解决方案。我们目前将会保留这个简单的HAProxy解决方案,因为它给与了我们一个对于如何管理容器集群的扎实的理解。

这是一个结束本系列的好地方。我可以说还有有许多额外的领域还没有涉及到,包括:
● 构建一个前端的容器,或者一个移动APP容器
● 包含后端的数据批处理过程
● 动态分级的容器集群来处理消息队列的条目
● 将服务从Java/Spring Boot迁移到Scala/Akka/Play
● 建立持续集成
● 构造自己的镜像Repository或者使用容器Repository服务(Google或者Docker Hub)
● 评估容器管理系统比如AWS的ECS或者Kubernetes


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART IV(翻译:胡震)

Spring Boot与Docker(三):构建你的第一个微服务和相关容器以及容器的连接

国会山上的猫TuxHu 发表了文章 • 2 个评论 • 5167 次浏览 • 2015-12-14 00:05 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包 ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治·华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

本篇构建微服务的步骤如下:

* 建立一个新的Spring Boot工程
* 定义我们的员工对象
* 持久连接
* 公开Web服务
* 定义一个Docker容器来运行我们的微服务,包括连接到我们在第二篇中创建的Mongo容器
* 在稍早设置的Docker Machine中运行我们的容器

建立我们的Spring Boot工程
我们将在我们的工程根目录下创建一个文件夹(生产环境中是在一个单独的版本库中)来承载我们的服务,在这个文件夹中,我们将创建build.grade文件。Spring Boot的优势就是在于对于依赖定义非常纯粹,如魔法般地带来了大量的互操作性。我们的build.grade文件看起来如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.RELEASE'
}
}
apply plugin: 'spring-boot'

repositories { jcenter()
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-web"
}

现在,正如我们提到的,我们将使用IDE,具体地说就是Spring Tool Suite(STS),所以我们将增加对于Gradle的支持。首先,打开STS,接着在打开的Dashboard页面上点击“IDE Extensions”的链接:
403x220xmicroservice-part-3-pic-1.jpg.pagespeed_.ic_.XIVhOVlbxA_.jpg

在屏幕上选择“Grade Support”,点击“Install”,按照提示完成安装,其中包括重启STS:
468x127xmicroservice-part-3-pic-2.jpg.pagespeed_.ic_.K4Ui_72tTm_.jpg

重启之后,选择“Import project…”,接着选择Gradle project(现在可用了),指向你的目录,然后点击“Build Model”——这将会用“Employee”来产生项目列表——选择这个列表并且点击“Finish”,这将会导入一个简单的build.grade到我们启动的工程。一旦工程被导入之后,第一个将是Spring Boot配置类,在这个场景中,我们将按照他们的服务来命名,所以在这种情况下配置类将会命名为EmployeeBoot。Spring大量使用了注解和反射,所以最小的配置也是需要的(忽略导入):
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class EmployeeBoot {

public static void main(String[] args) {
SpringApplication.run(EmployeeBoot.class);
}
}

我们的Employee类
接下来,我们将使得我们的POJO类来持有员工信息,我们目前保持最少的字段,有必要的话我们将增加:
Employee.Class

@Document(collection=”employees”)
public class Employee {

@Id
private String id;

private String email;
private String fullName;
private String managerEmail;

// getters and setters omitted for brevity
}

注意@Document这个注解——这会将这个对象连结为一个Mongo文档,并为该Employee“文档”在哪里存储指定了集合名字。
接下来我们将定义一个Spring持久类来读取这些Employee:
EmployeeRepository.class:
public interface EmployeeRepository extends
MongoRepository {
}

Spring框架的一个优美之处就是在于所有你需要写的——比如扩展自MongoRepository的接口甚至不需要手动编写实现代码。两个通用的参数中第一个表示需要持久化的对象类型(Employee),第二个表示唯一的标识符类型(String类型)。Spring框架将会自动提供所声明的功能95%的实现。接下来的问题就是:Spring如何知道数据库的位置?Spring默认情况下将会寻找localhost:27017,这显然是不会工作的,所以我们需要直接设置位置新型。我们可以实现自己的Mongo模板Bean,但是幸运的是Spring允许我们通过Java属性表传递连接信息。我们可以定义一个属性文件或者在命令行上传递进来。我们选择了后者因为当稍后构建我们的容器时命令行方式非常方便。最后我们需要创建的是一个或者两个Rest端点并确保它们可以工作。我们将构建一个快速的Spring Controller,然后我们就可以测试了。
EmployeeController.java

@RestController
@RequestMapping("/employee")
public class EmployeeController {

@Autowired
EmployeeRepository employeeRepository;

@RequestMapping(method = RequestMethod.POST)
public Employee create(@RequestBody Employee employee){

Employee result = employeeRepository.save(employee);
return result;
}

@RequestMapping(method = RequestMethod.GET, value="/{employeeId}")
public Employee get(@PathVariable String employeeId){
return employeeRepository.findOne(employeeId);
}
}

最开始的@RestController和@RequestMapping这两个类级别的注解告诉Spring框架需要公开这是一个接收JSON的Rest服务,并且公开了URI路径是/employee。@Autowired注解告诉Spring框架采用上面我们定义的Repository接口的自动生成实现代码并将其注入到这个Controller中。现在到了特定的操作——方法级别的@RequestMapping注解表明这个方法将会基于HTTP动词来使用(在本例中是POST和GET),另外对于GET操作,我们指明了一个URL路径{employee},比如使用/employee/abcd1234来寻找一个员工并且返回该值。
现在我们有了足够多的准备工作可以来测试了,第一点,我们需要编译和运行我们的Spring Boot应用,在Eclipse中有很多方法可以做到这一点,但是我们将从命令行开始,并以我们的方式工作。在你的Employee目录中,敲入:`gradle build`,这个命令将会编译Spring Boot应用到build/lib/Employee.jar,这个jar包包含了运行这个应用所需要的所有东西,包括一个嵌入式的Servlet容器(默认情况下是Tomcat)。
在我们运行和测试这个应用之前,我们需要稍作回顾——我们的Mongo服务又在哪里?观察“docker ps”命令的输出,我们记得虚拟机的32777端口映射到了Mongo容器的27017端口,虚拟机的IP地址是192.126.99.100。如前所述,我们可以通过传递一个环境变量属性的方式来把连接属性传递给Spring,所以运行这个应用的命令行如下:
java -Dspring.data.mongodb.uri=mongodb://192.168.99.100:32777/micros -jar build/libs/Employee.jar

一旦应用启动之后(应该在1~4秒),你可以使用选项中的Rest工具来打开Web服务。

456x171xmicroservice-part-3-pic-3.jpg.pagespeed_.ic_.wWMISBC68u_.jpg

不要忘了在HTTP头部包含值为“application/json”的“Content-Type”,你应该收到如下的响应(id的值取决于你自己的情况):
{
"id": "55fb2f1930e07c6c844b02ff",
"email": "dan.greene@3pillarglobal.com",
"fullName": "Daniel Greene",
"managerEmail": null
}

你可以通过如下的调用测试我们的GET方法:
http://localhost:8080/employee/55fb2f1930e07c6c844b02ff

你应该得到相同的文档,万岁!我们的服务起作用了!

将Boot应用导入容器
现在我们需要构建我们的第一个容器来运行我们的Employee微服务。我们的途径是定义一个Dockerfile,这个文件将会定义如何生成一个镜像并放入我们的精益求精、短小精悍的微服务中。让我们阅读这个文件并逐步解析(参考第三篇第三步请跳到这儿):
FROM java:8
VOLUME /tmp
ADD build/libs/Employee.jar app.jar
EXPOSE 8080
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Dspring.data.mongodb.uri=mongodb://mongodb/micros", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]


* 我们从一个包含了安装了Java 8的标准镜像(镜像名字为“Java”,标签为“8”)开始构建过程
* 我们接着定义了一个名为/tmp的卷
* 接着将本地文件系统的一个文件添加进来,并且重命名为“app.jar”,重命名不是必须的,只是一个可用的可选项
* 我们声明想要公开容器的8080端口
* 在容器内运行一个touch命令,这样可以确保app.jar文件的修改日期
* ENTRYPOINT命令定义了容器启动时需要运行的内容——我们运行Java,设置我们的Spring Mongo属性,还有快速的附加属性来加速Tomcat启动时间,然后指向我们的jar包。

现在我们通过运行如下命令构建镜像:
docker build -t microservicedemo/employee . 

我们可以敲入`docker images`看到结果
REPOSITORY                  TAG             IMAGE ID            CREATED             VIRTUAL SIZE
microservicedemo/employee latest 364ffd8162b9 15 minutes ago 846.6 MB


接下来的问题就是“我们的服务容器如何与Mongo容器交互?”,为此,我们引入了容器的link机制。当你运行一个新的容器时,你可以传入一个可选的-link参数指定一个运行中的容器的名字,这样新的容器就可以与这个容器通讯了,所以我们的命令是:

docker run -P -d --name employee --link mongodb microservicedemo/employee


我们启动了一个新的容器,公开了端口(-P),以后台方式运行(-d),命名为employee(—name),接着将这个新的容器连接到了一个名为“mongodb”的容器(-link),连接过程如下:

* 在employee容器的host文件添加一个条目指向MongoDB容器的运行位置
* 在employee容器内添加一些环境变量来协助其他必要的编程访问,可以运行如下命令查看:
docker exec employee bash -c 'env | grep MONGODB'

* 允许容器通过公开的端口来直接通讯,这样就不需要担心主机的部分映射了。如果你还记得上述的内容,我们设置了Spring Mongo到MongoDB的URL作为主机名(mongodb://mongodb/micros),所以有了host文件条目和运行在默认端口的Mongo,Boot应用容器可以连上数据库了

在容器运行时我们可以来执行相同的Web服务,只是这次是作为容器来运行的(对于我来说,容器的8080端口会被映射到虚拟机的32772端口):
467x483xmicroservice-part-3-pic-4.jpg.pagespeed_.ic_.2e11ONwBrl_.jpg

本篇我们已经取得了很大的进展,我们有了两个容器可以工作并且可以互相通讯,接下来的第四篇我们将添加一些额外的服务/容器,可以观察到构建更新的进度并与CI工具集成工作。


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART III(翻译:胡震)

Spring Boot与Docker(二):使用Spring Boot和Docker构建微服务架构

国会山上的猫TuxHu 发表了文章 • 1 个评论 • 10700 次浏览 • 2015-12-09 13:25 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第二篇,本篇我们将会利用工具进行设置,深入探讨如何使用Docker工作,然后搭建我们的第一个容器。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Gre ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第二篇,本篇我们将会利用工具进行设置,深入探讨如何使用Docker工作,然后搭建我们的第一个容器。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治·华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

介绍和工具

在本篇中,我们将会构建第一篇中讨论的解决方案。我是在Mac上工作的,但是工具在Mac和PC上是相同的,所以在平台上的操作是99%相同的,我将不会回顾如何安装这些工具,而是直接从如何使用它们开始,你所需要的准备工作如下:

* Docker Toolbox:包含了VirtualBox(作用为创建虚拟机用来运行容器)、Docker Machine(在虚拟机内运行Docker容器)、Docker Kitematic(一个管理在Docker Machine里运行的容器的GUI)以及Docker Compose(多容器编排工具)
* Git:我的GitHub链接在这儿,我在Windows上使用Git Extensions,在Mac上使用Source Tree,不过使用其他Git客户端包括命令行都是可以的。
* Java 8 SDK:Java 8在JVM永久代(PermGen)方面有了改进,另外对于Streams API和Lambda的支持也是非常赞的。
* 构建工具的选择:让我们使用Gradle吧,我推荐使用SDKMan,正式名称为GVM来管理Gradle版本,如果你使用Windows工作,你可以使用Cygwin安装SDKMan或者SDKMan的Powershell命令行工具,或者Gravy也是可替代的选择。
* IDE的选择:我们使用Spring Tool Suite(STS)来工作,在本文写作时最新的for Mac版本为3.7.0。
* Rest工具:对于任何Web服务项目使用都非常方便,我是Chrome扩展工具Postman的粉丝,如果你擅长curl命令行,这个工具也是不错的。
* uMongo或者Mongo GUI:文档型数据库完美匹配自足性的模型——对象进行自动检索,并且参考对象可以在微服务架构中通过ID来引用,这些ID可以很好地映射到文档存储中,另外地,MongoDB拥有运行很棒的“官方”Docker镜像。

我们对于源代码管理的第一个意见——似乎也是绝大多数的在线观点,就是每个微服务都应该有自己的版本库。微服务的一个基本理念就是跨服务之间不能共享代码。就我个人而言,这对我架构师的小心脏是个小小的打击,因为任何实用工具的重复代码的数量可能会比较多,以及缺乏一个单一、统一的领域模型给我有点心痛的感觉。我理解这个原则——自足性意味着自力更生。为了这篇博文,我把所有的代码都放到了一个单独的代码库,然而,每个微服务在根目录下都有它一个自己的目录。这样做是为了让我可以随着时间的推移展示分支的进展。在一个真实的解决方案中,你应该让每一个微服务都有它自己的不同的版本库,也许会有统一的版本库引用其它的子模块。

总体思路

因为我们要处理隔离的、可重用的组件,我们将做以下的映射:
一个逻辑业务对象→一个微服务→一个Git版本库目录→一个Mongo集合
因为业务对象可能由多个对象组成,任何我们认为可以作为其自身业务对象的子对象都可以划分为其自身的组件栈。

更多有关Docker如何工作的信息,以及我们的第一个容器

为了理解如何构建一个完整的基于Docker容器的产品解决方案,我们将需要深入研究容器是如何在宿主机(或者是虚拟机,正如我们的例子)里运行的,使用Docker通常是有三个阶段:镜像构建、镜像分发和容器部署。

构建镜像——Dockerfile的世界

为了构建镜像,你要写一系列的指令用来获取已有的镜像,接着对该镜像做些改变和配置。官方的Docker Hub Repository包含了许多的“官方”镜像以及数以千计的用户定制的镜像。如果其中的镜像不太符合你的需求,你可以创建一个定制的Dockerfile,用来在镜像上逐步添加一些内容,,比如安装系统软件包、复制文件或者公开一些网络端口,当我们构建微服务时,我们将会创建一个定制的Dockerfile,不过现在,我们将会利用一个标准镜像来启动一个MongoDB实例。

容器联网

当容器启动时,有一个它私有的网络,容器宿主机端口将外部网络通信转发到单个的容器端口上,特定的容器端口可以通过Dockerfile来决定公开,并且端口转发可以通过以下两种方式之一来进行:你可以显式地从宿主机映射端口到容器上,或者如果没有显式映射的话,Docker将映射已声明的容器端口到宿主机一个可用的临时端口上(通常的范围是从32768到61000)。当我们可以对整个解决方案显式地管理端口映射时,通常让Docker处理这一切是一个更加好的主意,并且可以通过Link机制公开端口信息到容器上,这方面将在我们构建我们的第一个微服务容器时有所涉及。

启动MongoDB容器

无论你是使用Kitematic还是Docker命令行,都可以非常简单的启动一个标准的容器。使用命令行的话,如果一切都安装正确,命令提示符将会包含以下三个关键的环境变量:
DOCKER_HOST=tcp://192.168.99.100:2376
DOCKER_MACHINE_NAME=default
DOCKER_TLS_VERIFY=1
DOCKER_CERT_PATH=/Users/[username]/.docker/machine/machines/default

这些是按照你的情况设置的(如果是在安装过程中打开的话,你可能需要重启终端或者命令提示符)。这些都是必要的,因为Docker不能直接运行在我的Mac笔记本上,而是跑在笔记本运行的虚拟机上。Docker客户端非常有效地将Docker命令从我的笔记本“代理”到虚拟机上,在我们启动容器前,让我们重温一下一部分Docker命令是非常有益的,在使用任何图形用户界面之前,了解命令行的东西总是很好的。

Docker级别的命令:

docker ps
该命令将会列出所有运行的容器,显示的信息包括它们的ID、名字、基础镜像名字和端口映射信息等。

docker build
该命令用来定义一个镜像——通过处理Dockerfile来创建一个新的镜像,我们将用这个命令来构建我们的微服务镜像。

docker pull[镜像名字]
该命令从远程Repository拉取镜像并且存储在本地。

docker run
该命令将基于一个本地或者远程Repository(比如Docker Hub)启动一个容器,我们将会相当多地探究这个命令。

docker push
该命令推送一个构建好的镜像到一个Repository,通常是Docker Hub。

容器特定的命令

这些命令使用容器ID或者名字作为一个参数:

docker status [容器名字/ID][容器名字/ID]
这个命令将显示指定的每一个容器的当前负载,比如CPU占用率、内存使用率以及网络流量等。

docker logs [-f][容器名字/ID]
该命令显示容器的最新的日志,-f选项就好比Shell终端中的“tail -f”中的-f选项。

docker inspect [容器名字/ID]
该命令将容器的所有配置信息以JSON的格式转储出来显示。

docker port [容器名字/ID]
该命令显示容器与宿主机之间的所有端口映射信息。

docker exec [-i][-t][容器名字/ID]
该命令将在目标容器上执行一条命令(-i表明以交互方式运行,-t表明以伪TTY终端运行),这个命令常用来获得一个容器终端Shell:
docker exec -it [容器名字/ID]sh

一旦我们理解了这些参考材料,我们可以进入到下一步启动一个Mongo容器。

命令非常简单:docker run -P -d —name mongo mongo

解释如下:

* P选项告知Docker在临时端口范围里公开容器声明的任何端口
* d选项告知以Daemon方式运行容器(比如在后台)
* name选项给容器分配一个名字(名字必须在所有运行的容器实例中唯一,如果你不提供这个选项,将会获得一个随机的半友好的名字比如modest_poitras)
* 最后的mongo表明了使用哪一个镜像

Docker Hub镜像的定义采用了[所有者]/[镜像名字][:标签]的命名格式,如果没有指定所有者,那么使用的就是“官方”的Docker Hub镜像——这是预留给Docker官方给软件供应商的礼物也就是成为官方镜像,如果最后的标签部分省略的话,那么就会认为你需要获得的是最新版本的镜像。

现在我们来尝试确认我们的Mongo实例已经启动并且运行了:
docker exec -it mongo sh
# mongo
MongoDB shell version: 3.0.6
connecting to: test
Server has startup warnings:
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten] ** We suggest setting it to 'never'
2015-09-02T00:57:30.761+0000 I CONTROL [initandlisten]
[quote] use microserviceblog
switched to db microserviceblog
> db.createCollection('testCollection')
{ "ok" : 1 }

在容器内部,Mongo看起来正在运行,但是我们可以从外部获知吗?为了尝试这个,我们需要查看什么临时端口被映射到了Mongo的端口上,我们运行如下命令:
docker ps

从下面我们可以看到(为了可读性省略了一些列):
CONTAINER ID        IMAGE                PORTS                      NAMES
87192b65de95 mongo 0.0.0.0:32777->27017/tcp mongodb

我们可以看到宿主机端口32777映射到了容器端口27017上,然而,记住我们的容器是运行在虚拟机上的,所以我们必须回到我们的环境变量:
$ echo $DOCKER_HOST
tcp://192.168.99.100:2376

我们应该可以通过如下的地址访问我们的Mongo容器的27017端口:
192.168.99.100:32777,启动Mongo然后点击那个位置显示数据库可以外部访问:
236x174xbuilding_microservice_part2.jpg.pagespeed_.ic_.-L7yvISwcS_.jpg

在这里结束第二篇吧,在本系列的第三篇中,我们将继续探讨,通过创建一个或两个微服务,管理它们的变化,然后运用持续集成以及产品部署技术进行工作。[/quote]

原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART II(翻译:胡震)

Spring Boot与Docker(一):微服务架构和容器化概述

国会山上的猫TuxHu 发表了文章 • 2 个评论 • 14169 次浏览 • 2015-12-07 23:35 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列四部曲的第一篇,本篇将会对我们谈及的微服务架构以及容器化概念作一个概述。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件 ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列四部曲的第一篇,本篇将会对我们谈及的微服务架构以及容器化概念作一个概述。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治·华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

有关微服务的所有那些喧闹到底是什么?

随着越来越多的产品利用可重用的基于Rest的服务构建,人们很快发现把业务功能拆分成为可重用的服务是非常有效的,但同时也带来了一个风险。每次更新服务,您必须重新测试与它一起部署的每一个服务,即使您有信心认为代码更改不会影响其他服务,您永远无法真正确保这一点,因为这些服务会不可避免地共享代码、数据和其他组件。

随着容器化的兴起,你可以在一个完全隔离的环境中非常高效地运行代码,把它们两个组合在一起将会允许在细粒度可扩展性和版本控制方面的产品架构优化,不过付出的代价是增加了复杂性和一些重复的代码。

容器化是不是仅仅只是新的虚拟化?

答案是不完全是。容器和虚拟机具有一些相似性,它们都是通过一个控制进程管理的隔离环境(分别是容器管理器和虚拟机监控程序),但两者之间的主要区别是,对于每一个虚拟机,运行的是一个完整的组件堆栈——从操作系统到应用服务器,以及仿真的虚拟硬件包括网络组件、CPU和内存。

对于容器来讲,它们运行在更为完全隔离的沙盒中,出现在每个容器里的仅仅是操作系统的最小内核,共享了底层系统的资源。容器化的最大优势在于对于相同的硬件占用空间更小,可以比虚拟机运行更多的实例。容器也有一些关键的限制:最大的一个是容器只能运行在基于Linux的操作系统上面(内核隔离是Linux的特定技术)。

与这一限制相关的就是Docker——目前最流行的容器服务提供系统——不能直接运行在Mac或者Windows系统上,因为它们不是Linux,替代方案就是为了运行Docker,你需要使用VirtualBox启动一个Linux虚拟机,接着在虚拟机里运行Docker。幸运的是,它绝大多数是由Docker ToolBox来管理的(原名Boot2Docker)。

Docker已经获得了众多的支持,以至于容器镜像的公共Repository——Docker Hub,拥有超过了136,000个公共镜像。其中许多是由个人创建的,一些扩展自“官方”镜像然后按照他们自己的需求做了定制,但是其它的都是从“基础”镜像做了完整的平台配置定制化。我们将利用这些“基础”和“官方”镜像开始我们的研究之旅。

所以我们已经谈论了微服务和容器化,但是Spring Boot在哪个部分起作用呢?

我选择使用Java来构建我自己的微服务,具体地说就是Spring Boot框架。选择它主要是因为我熟悉Spring、易于开发的Rest服务Controller、业务服务以及数据存储,还可以很容易地引入Scala的Akka/Play编程模型。微服务架构的其中一个最为人称道的优势就是服务的完全独立,所以不需要也不应该关心每一个服务采用什么语言或平台构建。

就我个人而言,我认为多语言的维护成本要比获得的弹性收益更大,但也有适用的用例,比如在一个大组织内的一个部门已经选择了不同的技术栈为“标准”的情况下。另外一个可能的场景就是如果你决定从一个语言/平台转换到另外一个——你可以每次迁移一个微服务,提供相同的终端Web服务接口。我们努力的目标如下:

* 一个从开始设置微服务和Docker到如何结束的指南。
* 了解围绕微服务架构的诸多组件的作出的不同决定的利弊——从源代码控制到服务版本化,以及其中的一切。
* 分析“纯粹”的微服务信仰,并且看它们如何应用到一个“现实世界”的场景。
* 看Docker是如何面对喧嚣,以及为专业开发运行Docker什么是必需的。
* 利用一系列的微服务构建一个完整的解决方案,每一个微服务都有它自己的容器,一个在自身容器托管的持久化层,还有容器集群。
* 其它有价值的内容。

我将模拟的业务场景是一家软件开发公司的员工任务分配和识别系统,包含了以下任务:

* 员工登录进系统
* 员工看到要求的任务列表,比如写一篇关于新兴技术的博文、参加一个会议、举行一次Code Review
* 员工向他们的经理提交这些任务的完成批准
* 员工获得完成任务的“打分”
* 员工依据“打分”可以获得奖励,比如公司礼物、与CEO一对一的免费午餐等等。

在这儿结束本篇非常不错——我们已经开始了理解微服务、容器化以及接下来用来讨论的业务场景,当我们步入第二篇时,我们将会设置相关的工具,深入如何使用Docker工作,然后搭建我们的第一个容器。

原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART I(翻译:胡震)

一张图了解Spring Cloud微服务架构

老马 发表了文章 • 0 个评论 • 590 次浏览 • 2019-04-26 20:48 • 来自相关话题

Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置 ...查看全部
Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。Spring Cloud中各个组件在微服务架构中扮演的角色如下图所示,黑线表示注释说明,蓝线由A指向B,表示B从A处获取服务。
6295401-8076ec880947ba79.png

Spring Cloud组成的微服务架构图

由上图所示微服务架构大致由上图的逻辑结构组成,其包括各种微服务、注册发现、服务网关、熔断器、统一配置、跟踪服务等。下面说说Spring Cloud中的组件分别充当其中的什么角色。

Fegin(接口调用):微服务之间通过Rest接口通讯,Spring Cloud提供Feign框架来支持Rest的调用,Feign使得不同进程的Rest接口调用得以用优雅的方式进行,这种优雅表现得就像同一个进程调用一样。

Netflix eureka(注册发现):微服务模式下,一个大的Web应用通常都被拆分为很多比较小的Web应用(服务),这个时候就需要有一个地方保存这些服务的相关信息,才能让各个小的应用彼此知道对方,这个时候就需要在注册中心进行注册。每个应用启动时向配置的注册中心注册自己的信息(IP地址,端口号, 服务名称等信息),注册中心将他们保存起来,服务间相互调用的时候,通过服务名称就可以到注册中心找到对应的服务信息,从而进行通讯。注册与发现服务为微服务之间的调用带来了方便,解决了硬编码的问题。服务间只通过对方的服务ID,而无需知道其IP和端口即可以获取对方方服务。

Ribbon(负载均衡):Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP客户端的行为。为Ribbon,配置服务提供者的地址列表后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。Ribbon默认为我们提供了很多的负载均衡算法,例如轮询、随机等。当然,我们也可为Ribbon实现自定义的负载均衡算法。在Spring Cloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从EurekaServer获取服务提供者的地址列表,并基于负载均衡算法,请求其中一个服务提供者的实例(为了服务的可靠性,一个微服务可能部署多个实例)。

Hystrix(熔断器):当服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载场景下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃(雪崩效应)。Hystrix正是为了防止此类问题发生。Hystrix是由Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。Hystrix主要通过以下几点实现延迟和容错。

* 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用了设计模式中的“命令模式”。
* 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
* 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。
* 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时和被拒绝的请求等。
* 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员指定。

Zuul(微服务网关):不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。例如一个电影购票的手机APP,可能调用多个微服务的接口才能完成一次购票的业务流程,如果让客户端直接与各个微服务通信,会有以下的问题:

* 客户端会多次请求不同的微服务,增加了客户端的复杂性。
* 存在跨域请求,在一定场景下处理相对复杂。
* 认证复杂,每个服务都需要独立认证。
* 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。如果客户端直接与微服务通信,那么重构将很难实施。
* 某些微服务可能使用了对防火墙/浏览器不友好的协议,直接访问时会有一定的困难。

以上问题可借助微服务网关解决。微服务网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过微服务网关。使用微服务网关后,微服务网关将封装应用程序的内部结构,客户端只用跟网关交互,而无须直接调用特定微服务的接口。这样,开发就可以得到简化。不仅如此,使用微服务网关还有以下优点:

* 易于监控。可在微服务网关收集监控数据并将其推送到外部系统进行分析。
* 易于认证。可在微服务网关上进行认证,然后再将请求转发到后端的微服务,而无须在每个微服务中进行认证。
* 减少了客户端与各个微服务之间的交互次数。

Spring Cloud Bus( 统一配置服务):对于传统的单体应用,常使用配置文件管理所有配置。例如一个SpringBoot开发的单体应用,可将配置内容放在application.yml文件中。如果需要切换环境,可设置多个Profile,并在启动应用时指定spring.profiles.active={profile}。然而,在微服务架构中,微服务的配置管理一般有以下需求:

* 集中管理配置。一个使用微服务架构的应用系统可能会包含成百上千个微服务,因此集中管理配置是非常有必要的。
* 不同环境,不同配置。例如,数据源配置在不同的环境(开发、测试、预发布、生产等)中是不同的。
* 运行期间可动态调整。例如,可根据各个微服务的负载情况,动态调整数据源连接池大小或熔断阈值,并且在调整配置时不停止微服务。
* 配置修改后可自动更新。如配置内容发生变化,微服务能够自动更新配置。综上所述,对于微服务架构而言,一个通用的配置管理机制是必不可少的,常见做法是使用配置服务器管理配置。Spring Cloud Bus利用Git或SVN等管理配置、采用Kafka或者RabbitMQ等消息总线通知所有应用,从而实现配置的自动更新并且刷新所有微服务实例的配置。

Sleuth+ZipKin(跟踪服务):Sleuth和Zipkin结合使用可以通过图形化的界面查看微服务请求的延迟情况以及各个微服务的依赖情况。需要注意的是Spring Boot 2及以上不在支持Zipkin的自定义,需要到官方网站下载ZipKin相关的jar包。另外需要提一点的是Spring Boot Actuator,提供了很多监控端点如/actuator/info、/actuator/health、/acutator/refresh等,可以查看微服务的信息、健康状况、刷新配置等。

原文链接:一张图了解Spring Cloud微服务架构(作者:SimpleEasy)

如何使用消息队列,Spring Boot和Kubernetes扩展微服务

新牛哥 发表了文章 • 0 个评论 • 1457 次浏览 • 2019-02-10 14:10 • 来自相关话题

【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。 当你设计和构建大规模应用时, ...查看全部
【编者的话】本文通过一个详细的购物例子,展示了如何利用消息队列,Spring Boot和Kubernetes进行微服务的开发,并阐述了针对微服务的伸缩,监控等方式,帮助用户快速利用这些工具开发健壮的系统。

当你设计和构建大规模应用时,你将面临两个重大挑战:可伸缩性和健壮性

你应该这样设计你的服务,即使它受到间歇性的重负载,它仍能可靠地运行。

以Apple Store为例。

每年都有数百万的Apple客户预先注册购买新的iPhone。

这是数百万人同时购买物品。

如果你要将Apple商店的流量描述为每秒的请求数量,那么它可能是下图的样子:
2.png

现在想象一下,你的任务是构建这样的应用程序。

你正在建立一个商店,用户可以在那里购买自己喜欢的商品。

你构建一个微服务来呈现网页并提供静态资产。 你还构建了一个后端REST API来处理传入的请求。

你希望将两个组件分开,因为这样可以使用相同的REST API,为网站和移动应用程序提供服务。
3.gif

今天是重要的一天,你的商店上线了。

你决定将应用程序扩展为前端四个实例和后端四个实例,因为你预测网站比平常更繁忙。
4.gif

你开始接收越来越多的流量。

前端服务正在很好得处理流量。 但是你注意到连接到数据库的后端正在努力跟上事务的数量。

不用担心,你可以将后端的副本数量扩展到8。
5.gif

你收到的流量更多,后端无法应对。

一些服务开始丢弃连接。 愤怒的客户与你的客服取得联系。 而现在你被淹没在大量流量中。

你的后端无法应付它,它会失去很多连接。
6.gif

你刚丢了一大笔钱,你的顾客也不高兴。

你的应用程序并没有设计得健壮且高可用:

  • 前端和后端紧密耦合——实际上它不能在没有后端的情况下处理应用
  • 前端和后端必须一致扩展——如果没有足够的后端,你可能会淹没在流量中
  • 如果后端不可用,则无法处理传入的事务。
失去事务意味着收入损失。你可以重新设计架构,以便将前端和后端用队列分离。
7.gif
前端将消息发布到队列,而后端则一次处理一个待处理消息。新架构有一些明显的好处:
  • 如果后端不可用,则队列充当缓冲区
  • 如果前端产生的消息多于后端可以处理的消息,则这些消息将缓冲在队列中
  • 你可以独立于前端扩展后端——即你可以拥有数百个前端服务和后端的单个实例
太好了,但是你如何构建这样的应用程序?你如何设计可处理数十万个请求的服务? 你如何部署动态扩展的应用程序?在深入了解部署和扩展的细节之前,让我们关注应用程序。#编写Spring应用程序该服务有三个组件:前端,后端和消息代理。前端是一个简单的Spring Boot Web应用程序,带有Thymeleaf模板引擎。后端是一个消耗队列消息的工作者。由于Spring Boot与JSM能出色得集成,因此你可以使用它来发送和接收异步消息。你可以在learnk8s / spring-boot-k8s-hpa中找到一个连接到JSM的前端和后端应用程序的示例项目。请注意,该应用程序是用Java 10编写的,以利用改进的Docker容器集成能力。只有一个代码库,你可以将项目配置为作为前端或后端运行。你应该知道该应用程序具有:
  • 一个购买物品的主页
  • 管理面板,你可以在其中检查队列中的消息数
  • 一个 `/health` 端点,用于在应用程序准备好接收流量时发出信号
  • 一个 `/submit` 端点,从表单接收提交并在队列中创建消息
  • 一个 `/metrics` 端点,用于公开队列中待处理消息的数量(稍后将详细介绍)
该应用程序可以在两种模式下运行:作为前端,应用程序呈现人们可以购买物品的网页。
8.png
作为工作者,应用程序等待队列中的消息并处理它们。
9.png
请注意,在示例项目中,使用`Thread.sleep(5000)`等待五秒钟来模拟处理。你可以通过更改`application.yaml`中的值来在任一模式下配置应用程序。#模拟应用程序的运行默认情况下,应用程序作为前端和工作程序启动。你可以运行该应用程序,只要你在本地运行ActiveMQ实例,你就应该能够购买物品并让系统处理这些物品。
10.gif
如果检查日志,则应该看到工作程序处理项目。它确实工作了!编写Spring Boot应用程序很容易。一个更有趣的主题是学习如何将Spring Boot连接到消息代理。#使用JMS发送和接收消息Spring JMS(Java消息服务)是一种使用标准协议发送和接收消息的强大机制。如果你以前使用过JDBC API,那么你应该熟悉JMS API,因为它的工作方式很类似。你可以按JMS方式来使用的最流行的消息代理是ActiveMQ——一个开源消息服务器。使用这两个组件,你可以使用熟悉的接口(JMS)将消息发布到队列(ActiveMQ),并使用相同的接口来接收消息。更妙的是,Spring Boot与JMS的集成非常好,因此你可以立即加快速度。实际上,以下短类封装了用于与队列交互的逻辑:
@Componentpublic 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());    }  }} 
你可以使用`send`方法将消息发布到命名队列。此外,Spring Boot将为每个传入消息执行`onMessage`方法。最后一个难题是指示Spring Boot使用该类。你可以通过在Spring Boot应用程序中注册侦听器来在后台处理消息,如下所示:
@SpringBootApplication@EnableJmspublic 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);  }} 
其中id是使用者的唯一标识符,destination是队列的名称。你可以从GitHub上的项目中完整地读取Spring队列服务的源代码。回顾一下你是如何在少于40行代码中编写可靠队列的。你一定很喜欢Spring Boot。# 你在部署时节省的所有时间都可以专注于编码你验证了应用程序的工作原理,现在是时候部署它了。此时,你可以启动VPS,安装Tomcat,并花些时间制作自定义脚本来测试,构建,打包和部署应用程序。或者你可以编写你希望拥有的描述:一个消息代理和两个使用负载均衡器部署的应用程序。诸如Kubernetes之类的编排器可以阅读你的愿望清单并提供正确的基础设施。由于花在基础架构上的时间减少意味着更多的时间编码,这次你将把应用程序部署到Kubernetes。但在开始之前,你需要一个Kubernetes集群。你可以注册Google云平台或Azure,并使用Kubernetes提供的云提供商服务。或者,你可以在将应用程序移动到云上之前在本地尝试Kubernetes。`minikube`是一个打包为虚拟机的本地Kubernetes集群。如果你使用的是Windows,Linux和Mac,那就太好了,因为创建群集需要五分钟。你还应该安装`kubectl`,即连接到你的群集的客户端。你可以从官方文档中找到有关如何安装`minikube`和`kubectl`的说明。

如果你在Windows上运行,则应查看有关如何安装Kubernetes和Docker的详细指南

你应该启动一个具有8GB RAM和一些额外配置的集群:
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`实例,则可以通过销毁VM来重新调整VM的大小。 只需添加`--memory 8096`就不会有任何影响。验证安装是否成功。 你应该看到以列表形式展示的一些资源。 集群已经准备就绪,也许你应该立即开始部署?还不行。你必须先装好你的东西。# 什么比uber-jar更好?容器部署到Kubernetes的应用程序必须打包为容器。毕竟,Kubernetes是一个容器编排器,所以它本身无法运行你的jar。容器类似于fat jar:它们包含运行应用程序所需的所有依赖项。甚至JVM也是容器的一部分。所以他们在技术上是一个更胖的fat-jar。将应用程序打包为容器的流行技术是Docker。虽然Docker是最受欢迎的,但它并不是唯一能够运行容器的技术。其他受欢迎的选项包括`rkt`和`lxd`。如果你没有安装Docker,可以按照Docker官方网站上的说明进行操作。通常,你构建容器并将它们推送到仓库。它类似于向Artifactory或Nexus推送jar包。但在这种特殊情况下,你将在本地工作并跳过仓库部分。实际上,你将直接在`minikube`中创建容器镜像。首先,按照此命令打印的说明将Docker客户端连接到`minikube`:
minikube docker-env
请注意,如果切换终端,则需要重新连接`minikube`内的Docker守护程序。 每次使用不同的终端时都应遵循相同的说明。并从项目的根目录构建容器镜像:
docker build -t spring-k8s-hp0a .
你可以验证镜像是否已构建并准备好运行:
docker images |grep spring 
很好。群集已准备好,你打包应用程序,也许你已准备好立即部署?是的,你最终可以要求Kubernetes部署应用程序。# 将你的应用程序部署到Kubernetes你的应用程序有三个组件:
  • 呈现前端的Spring Boot应用程序
  • ActiveMQ作为消息代理
  • 处理事务的Spring Boot后端
你应该分别部署这三个组件。对于每个组件你都应该创建:
  • Deployment对象,描述部署的容器及其配置
  • 一个Service对象,充当Deployment部署创建的应用程序的所有实例的负载均衡器
部署中的每个应用程序实例都称为Pod
11.gif
# 部署ActiveMQ让我们从ActiveMQ开始吧。你应该创建一个`activemq-deployment.yaml`文件,其中包含以下内容:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: queuespec:  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的内存
  • 你要求提供单个副本 - 你的应用程序的单个实例
使用以下内容创建`activemq-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: queuespec:  ports:  - port: 61616     targetPort: 61616  selector:    app: queue
幸运的是,这个模板更短!这个yaml表示:
  • 你创建了一个公开端口61616的负载均衡器
  • 传入流量分发到所有具有`app:queue`类型标签的Pod(请参阅上面的部署)
  • `targetPort`是Pod暴露的端口
你可以使用以下命令创建资源:
kubectl create -f activemq-deployment.yamlkubectl create -f activemq-service.yaml
你可以使用以下命令验证数据库的一个实例是否正在运行:
kubectl get pods -l=app=queue
# 部署前端使用以下内容创建`fe-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: frontendspec:  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
Deployment 看起来很像前一个。但是有一些新的字段:
  • 有一个section可以注入环境变量
  • 还有Liveness探针,可以告诉你应用程序何时可以接受流量
使用以下内容创建`fe-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: frontendspec:  ports:  - nodePort: 32000    port: 80    targetPort: 8080  selector:    app: frontend  type: NodePort
你可以使用以下命令创建资源:
kubectl create -f fe-deployment.yamlkubectl create -f fe-service.yaml
你可以使用以下命令验证前端应用程序的一个实例是否正在运行:
kubectl get pods -l=app=frontend
# 部署后端使用以下内容创建`backend-deployment.yaml`文件:
apiVersion: extensions/v1beta1kind: Deploymentmetadata:  name: backendspec:  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
使用以下内容创建`backend-service.yaml`文件:
apiVersion: v1kind: Servicemetadata:  name: backend  spec:    ports:    - nodePort: 31000      port: 80      targetPort: 8080    selector:      app: backend    type: NodePort
你可以使用以下命令创建资源:
kubectl create -f backend-deployment.yamlkubectl create -f backend-service.yaml
你可以验证后端的一个实例是否正在运行:
kubectl get pods -l=app=backend
部署完成。它真的有效吗?你可以使用以下命令在浏览器中访问该应用程序:
minikube service backend
minikube service frontend
如果它有效,你应该尝试购买一些物品!工作者在处理交易吗?是的,如果有足够的时间,工作人员将处理所有待处理的消息。恭喜!你刚刚将应用程序部署到Kubernetes!# 手动扩展以满足不断增长的需求单个工作程序可能无法处理大量消息。 实际上,它当时只能处理一条消息。如果你决定购买数千件物品,则需要数小时才能清除队列。此时你有两个选择:
  • 你可以手动放大和缩小
  • 你可以创建自动缩放规则以自动向上或向下扩展
让我们先从基础知识开始。你可以使用以下方法将后端扩展为三个实例:
kubectl scale --replicas=5 deployment/backend
你可以验证Kubernetes是否创建了另外五个实例:
kubectl get pods
并且应用程序可以处理五倍以上的消息。一旦工人排空队列,你可以缩小:
kubectl scale --replicas=1 deployment/backend
如果你知道最多的流量何时达到你的服务,手动扩大和缩小都很棒。如果不这样做,设置自动缩放器允许应用程序自动缩放而无需手动干预。你只需要定义一些规则。# 公开应用程序指标Kubernetes如何知道何时扩展你的申请?很简单,你必须告诉它。自动调节器通过监控指标来工作。 只有这样,它才能增加或减少应用程序的实例。因此,你可以将队列长度公开为度量标准,并要求autoscaler观察该值。 队列中的待处理消息越多,Kubernetes将创建的应用程序实例就越多。那么你如何公开这些指标呢?应用程序具有 `/metrics` 端点以显示队列中的消息数。 如果你尝试访问该页面,你会注意到以下内容:
# HELP messages Number of messages in the queue # TYPE messages gaugemessages 0
应用程序不会将指标公开为JSON格式。 格式为纯文本,是公开Prometheus指标的标准。 不要担心记忆格式。 大多数情况下,你将使用其中一个Prometheus客户端库。# 在Kubernetes中使用应用程序指标你几乎已准备好进行自动缩放——但你应首先安装度量服务器。 实际上,默认情况下,Kubernetes不会从你的应用程序中提取指标。 如果你愿意,可以启用Custom Metrics API。要安装Custom Metrics API,你还需要Prometheus - 时间序列数据库。 安装Custom Metrics API所需的所有文件都可以方便地打包在learnk8s / spring-boot-k8s-hpa中。你应下载该存储库的内容,并将当前目录更改为该项目的`monitoring`文件夹。
cd spring-boot-k8s-hpa/monitoring
从那里,你可以创建自定义指标API:
kubectl create -f ./metrics-serverkubectl create -f ./namespaces.yamlkubectl create -f ./prometheuskubectl 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 .
恭喜,你有一个公开指标的应用程序和使用它们的指标服务器。你最终可以启用自动缩放器!# 在Kubernetes中进行自动扩展部署Kubernetes有一个名为Horizontal Pod Autoscaler的对象,用于监视部署并上下调整Pod的数量。你将需要其中一个来自动扩展实例。你应该创建一个包含以下内容的`hpa.yaml`文件:
apiVersion: autoscaling/v2beta1kind: HorizontalPodAutoscalermetadata:  name: spring-boot-hpaspec:  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
自动定标器表示它能够将Pod扩展到2,并且它已准备好监视部署。令人兴奋的东西,但它有效吗?# 负载测试只有一种方法可以知道它是否有效:在队列中创建大量消息。转到前端应用程序并开始添加大量消息。 在添加消息时,使用以下方法监视Horizontal Pod Autoscaler的状态:
kubectl describe hpa
Pod的数量从2上升到4,然后是8,最后是10。该应用程序随消息数量而变化! 欢呼!你刚刚部署了一个完全可伸缩的应用程序,可根据队列中的待处理消息数进行扩展。另外,缩放算法如下:
MAX(CURRENT_REPLICAS_LENGTH * 2, 4)
在解释算法时,文档没有多大帮助。 你可以在代码中找到详细信息。此外,每分钟都会重新评估每个放大,而每两分钟缩小一次。以上所有都是可以调整的设置。但是你还没有完成。# 什么比自动缩放实例更好? 自动缩放群集。跨节点缩放Pod非常有效。 但是,如果群集中没有足够的容量来扩展Pod,该怎么办?如果达到峰值容量,Kubernetes将使Pods处于暂挂状态并等待更多资源可用。如果你可以使用类似于Horizontal Pod Autoscaler的自动缩放器,但对于节点则会很棒。好消息!你可以拥有一个群集自动缩放器,可以在你需要更多资源时为Kubernetes群集添加更多节点。
12.gif
群集自动缩放器具有不同的形状和大小。它也是特定于云提供商的。请注意,你将无法使用`minikube`测试自动缩放器,因为它根据定义是单节点。你可以在Github上找到有关集群自动调节器和云提供程序实现的更多信息。# 概览设计大规模应用程序需要仔细规划和测试。基于队列的体系结构是一种出色的设计模式,可以解耦你的微服务并确保它们可以独立扩展和部署。虽然你可以用脚本来扩展你的应用,但可以更轻松地利用容器编排器(如Kubernetes)自动部署和扩展应用程序。# 今天先说到这里感谢Nathan Cashmore和Andy Griffiths的反馈!如果你喜欢这篇文章,下面的文章可能你也会有兴趣:
  • 3个简单的技巧,用于较小的Docker镜像,并学习如何更快地构建和部署Docker镜像。
  • Kubernetes Chaos Engineering:经验教训 - 第1部分Kubernetes出现问题时会发生什么? Kubernetes可以从失败和自我愈合中恢复吗?
#成为Kubernetes中部署和扩展应用程序的专家学习如何使用Horizo​​ntal Pod Autoscaler部署和扩展应用程序只是一个开始!从我们的实践课程开始,学习如何掌握Kubernetes。了解如何:
  • 不费工夫得处理最繁忙的流量网站
  • 将你的工作扩展到数千台服务器,并将等待时间从几天缩短到几分钟
  • 知道你的应用程序具有多云设置的高可用性,让你高枕无忧
  • 只需使用你需要的资源,就可以节省大量现金
  • 为你的交付管道增压并全天候部署应用程序

成为Kubernetes的专家

原文链接:How to scale Microservices with Message Queues, Spring Boot, and Kubernetes (翻译:池剑锋)

Spring Boot Tomcat 容器化部署实践与总结

wise2c 发表了文章 • 0 个评论 • 1833 次浏览 • 2018-09-06 18:19 • 来自相关话题

在平时的工作和学习中经常会构建简单的web应用程序。如果只是HelloWorld级别的程序,使用传统的Spring+SpringMVC框架搭建得话会将大部分的时间花费在搭建框架本身上面,比如引入SpringMVC,配置DispatcheherServlet等。 ...查看全部
在平时的工作和学习中经常会构建简单的web应用程序。如果只是HelloWorld级别的程序,使用传统的Spring+SpringMVC框架搭建得话会将大部分的时间花费在搭建框架本身上面,比如引入SpringMVC,配置DispatcheherServlet等。并且这些配置文件都差不多,重复这些劳动似乎意义不大。所以使用Springboot框架来搭建简单的应用程序显得十分的便捷和高效。
微信图片_20180906110558.png


前两天在工作中需要一个用于测试文件下载的简单web程序,条件是使用Tomcat Docker Image作为载体,所以为了方便就使用了SpringBoot框架快速搭建起来。

程序写出来在本机能够正常的跑起来,准备制作镜像,但是闻题就接踵而来了。首先是部署的问题,SpringBoot Web程序默认打的是jar包,运行时使用命令 java -jar -Xms128m -Xmx128m xxx.jar,本机跑的没问题。但是需求是使用外部的tomcat容器而不是tomcat-embed,所以查阅官方文档如下:

The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure method. Doing so makes use of Spring Framework’s Servlet 3.0 support and lets you configure your application when it is launched by the servlet container. Typically, you should update your application’s main class to extend SpringBootServletInitializer, as shown in the following example:
 @SpringBootApplication
public class Application extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}

}

The next step is to update your build configuration such that your project produces a war file rather than a jar file. If you use Maven and spring-boot-starter-parent(which configures Maven’s war plugin for you), all you need to do is to modify pom.xml to change the packaging to war, as follows:
war

If you use Gradle, you need to modify build.gradle to apply the war plugin to the project, as follows:
apply plugin: 'war'

The final step in the process is to ensure that the embedded servlet container does not interfere with the servlet container to which the war file is deployed. To do so, you need to mark the embedded servlet container dependency as being provided.

If you use Maven, the following example marks the servlet container (Tomcat, in this case) as being provided:
 


org.springframework.boot
spring-boot-starter-tomcat
provided




If you use Gradle, the following example marks the servlet container (Tomcat, in this case) as being provided:
 dependencies {
// …
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
// …
}

综上所述,将SpringBoot程序放入Tomcat运行有两步。第一,SpringBoot启动类继承SpringBootServletInitializer,重写configure方法。第二,将包管理软件的打包方式改成war,并将Spring-boot-starter-tomcat设置为provided。但是,为什么应该这么做?

根据Servlet3.0规范可知,Web容器启动时通过ServletContainerInitializer类实现第三方组件的初始化工作,如注册servlet或filter等,每个框架要是用ServletContainerInitializer就必须在对应的META-INF/services目录下创建名为javax.servlet.ServletContainerInitializer的文件,文件内容指定具体的ServletContainerInitializer实现类,在SpringMVC框架中为SpringServletContainerInitializer。一般伴随着ServletContainerInitializer一起使用的还有HandlesTypes注解,通过HandlesTypes可以将感兴趣的一些类注入到ServletContainerInitializerde的onStartup方法作为参数传入。如下为SpringServletContainerInitializer源代码:
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(@Nullable Set> webAppInitializerClasses, ServletContext
servletContext)throws ServletException {
List initializers = new LinkedList<>();
if (webAppInitializerClasses != null) {
for (Class waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers())
&&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
// 将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型
的类都传入到onStartup方法的Set>;为这些
WebApplicationInitializer类型的类创建实例。
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass)
.newInstance());
}
catch (Throwable ex) {
throw new ServletException("Failed to instantiate
WebApplicationInitializer class", ex);
}
}
}
}

if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected
on classpath");
return;
}

servletContext.log(initializers.size() + " Spring WebApplicationInitializers
detected on classpath");
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
//为每个WebApplicationInitializer调用自己的onStartup()
initializer.onStartup(servletContext);
}
}

}

SpringBootInitializer继承WebApplicationInitializer,重写的onStartup如下:
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
this.logger = LogFactory.getLog(getClass());
// 调用自生createRootApplicationContext()方法
WebApplicationContext rootAppContext = createRootApplicationContext(
servletContext);
if (rootAppContext != null) {
servletContext.addListener(new ContextLoaderListener(rootAppContext) {
@Override
public void contextInitialized(ServletContextEvent event) {
}
});
}
else {
this.logger.debug("No ContextLoaderListener registered, as "
+ "createRootApplicationContext() did not "
+ "return an application context");
}
}
protected WebApplicationContext createRootApplicationContext(
ServletContext servletContext) {
SpringApplicationBuilder builder = createSpringApplicationBuilder();
builder.main(getClass());
ApplicationContext parent = getExistingRootWebApplicationContext(servletContext);
if (parent != null) {
this.logger.info("Root context already created (using as parent).");
servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, null);
builder.initializers(new ParentContextApplicationContextInitializer(parent));
}
builder.initializers(
new ServletContextApplicationContextInitializer(servletContext));
builder.contextClass(AnnotationConfigServletWebServerApplicationContext.class);
// 调用重写方法,重写方法传入SpringBoot启动类
builder = configure(builder);
builder.listeners(new WebEnvironmentPropertySourceInitializer(servletContext));
SpringApplication application = builder.build();
if (application.getAllSources().isEmpty() && AnnotationUtils
.findAnnotation(getClass(), Configuration.class) != null) {
application.addPrimarySources(Collections.singleton(getClass()));
}
Assert.state(!application.getAllSources().isEmpty(),
"No SpringApplication sources have been defined. Either override the "
+ "configure method or add an @Configuration annotation");
if (this.registerErrorPageFilter) {
application.addPrimarySources(
Collections.singleton(ErrorPageFilterConfiguration.class));
}
//启动应用程序,就是启动传入的SpringBoot程序
return run(application);
}

在程序和Tomcat打通之后需做的就是将war打成一个Docker镜像,如果每次都是复制war包,然后再docker build会很麻烦,在开源社区早有了解决方案–docker-maven-plugin,查看Github中的使用方法,将如下内容加入pom.xml中:

com.spotify
docker-maven-plugin
1.1.1

wanlinus/file-server




${project.basedir}



/
${project.build.directory}
${project.build.finalName}.war





该配置中有个标签是用来指定构建docker image的Dockerfile的位置,在项目的根目录下新建一个Dockerfile,内容如下:

FROM tomcat
MAINTAINER wanlinus
WORKDIR /docker
COPY target/file-server-0.0.1-SNAPSHOT.war ./server.war
RUN mkdir $CATALINA_HOME/webapps/server \
&& mv /docker/server.war $CATALINA_HOME/webapps/server \
&& unzip $CATALINA_HOME/webapps/server/server.war -d $CATALINA_HOME/webapps/server/ \
&& rm $CATALINA_HOME/webapps/server/server.war \
&& cd $CATALINA_HOME/webapps/server && echo "asd" > a.txt
EXPOSE 8080


终端中输入
mvn clean package docker:build


在本地将会生成一个docker image,如果docker没有运行于本地,需要在标签中输入远端地址和docker daemon端口。

最后在终端中运行
docker run --rm -p 8080:8080 wanlinus/fileserver


在Tomcat启动后将会看到Spring Boot程序的启动日志,至此,Spring Boot Tomcat容器化完成。

原文链接:https://mp.weixin.qq.com/s/Vb4VzO7AT9PZ6QPtCe4SLw

分布式实时日志分析解决方案 ELK 部署架构

Java高级开发 发表了文章 • 0 个评论 • 1558 次浏览 • 2018-03-25 17:19 • 来自相关话题

一、概述 ELK 已经成为目前最流行的集中式日志解决方案,它主要是由Beats、Logstash、Elasticsearch、Kibana等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案。本文将会介绍ELK常见的架 ...查看全部
一、概述

ELK 已经成为目前最流行的集中式日志解决方案,它主要是由Beats、Logstash、Elasticsearch、Kibana等组件组成,来共同完成实时日志的收集,存储,展示等一站式的解决方案。本文将会介绍ELK常见的架构以及相关问题解决。

Filebeat:Filebeat是一款轻量级,占用服务资源非常少的数据收集引擎,它是ELK家族的新成员,可以代替Logstash作为在应用服务器端的日志收集引擎,支持将收集到的数据输出到Kafka,Redis等队列。 Logstash:数据收集引擎,相较于Filebeat比较重量级,但它集成了大量的插件,支持丰富的数据源收集,对收集的数据可以过滤,分析,格式化日志格式。 Elasticsearch:分布式数据搜索引擎,基于Apache Lucene实现,可集群,提供数据的集中式存储,分析,以及强大的数据搜索和聚合功能。 Kibana:数据的可视化平台,通过该web平台可以实时的查看 Elasticsearch 中的相关数据,并提供了丰富的图表统计功能。

二、ELK常见部署架构

2.1、Logstash作为日志收集器

这种架构是比较原始的部署架构,在各应用服务器端分别部署一个Logstash组件,作为日志收集器,然后将Logstash收集到的数据过滤、分析、格式化处理后发送至Elasticsearch存储,最后使用Kibana进行可视化展示,这种架构不足的是:Logstash比较耗服务器资源,所以会增加应用服务器端的负载压力。

2.2、Filebeat作为日志收集器

该架构与第一种架构唯一不同的是:应用端日志收集器换成了Filebeat,Filebeat轻量,占用服务器资源少,所以使用Filebeat作为应用服务器端的日志收集器,一般Filebeat会配合Logstash一起使用,这种部署方式也是目前最常用的架构。

2.3、引入缓存队列的部署架构

该架构在第二种架构的基础上引入了Kafka消息队列(还可以是其他消息队列),将Filebeat收集到的数据发送至Kafka,然后在通过Logstasth读取Kafka中的数据,这种架构主要是解决大数据量下的日志收集方案,使用缓存队列主要是解决数据安全与均衡Logstash与Elasticsearch负载压力。

2.4、以上三种架构的总结

第一种部署架构由于资源占用问题,现已很少使用,目前使用最多的是第二种部署架构,至于第三种部署架构个人觉得没有必要引入消息队列,除非有其他需求,因为在数据量较大的情况下,Filebeat 使用压力敏感协议向 Logstash 或 Elasticsearch 发送数据。如果 Logstash 正在繁忙地处理数据,它会告知 Filebeat 减慢读取速度。拥塞解决后,Filebeat 将恢复初始速度并继续发送数据。

三、问题及解决方案

问题:如何实现日志的多行合并功能?

系统应用中的日志一般都是以特定格式进行打印的,属于同一条日志的数据可能分多行进行打印,那么在使用ELK收集日志的时候就需要将属于同一条日志的多行数据进行合并。

解决方案:使用Filebeat或Logstash中的multiline多行合并插件来实现

在使用multiline多行合并插件的时候需要注意,不同的ELK部署架构可能multiline的使用方式也不同,如果是本文的第一种部署架构,那么multiline需要在Logstash中配置使用,如果是第二种部署架构,那么multiline需要在Filebeat中配置使用,无需再在Logstash中配置multiline。

1、multiline在Filebeat中的配置方式:

filebeat.prospectors:

  • paths:
  • /home/project/elk/logs/test.log
input_type: logmultiline:pattern: '^['negate: truematch: afteroutput: logstash:hosts: ["localhost:5044"]pattern:正则表达式 negate:默认为false,表示匹配pattern的行合并到上一行;true表示不匹配pattern的行合并到上一行 match:after表示合并到上一行的末尾,before表示合并到上一行的行首 如:pattern: '['negate: truematch: after该配置表示将不匹配pattern模式的行合并到上一行的末尾2、multiline在Logstash中的配置方式input {beats {port => 5044}}filter {multiline {pattern => "%{LOGLEVEL}s*]"negate => truewhat => "previous"}}output {elasticsearch {hosts => "localhost:9200"}}(1)Logstash中配置的what属性值为previous,相当于Filebeat中的after,Logstash中配置的what属性值为next,相当于Filebeat中的before。(2)pattern => “%{LOGLEVEL}s*]” 中的LOGLEVEL是Logstash预制的正则匹配模式,预制的还有好多常用的正则匹配模式,详细请看:https://github.com/logstash-plugins/logstash-patterns-core/tree/master/patterns问题:如何将Kibana中显示日志的时间字段替换为日志信息中的时间?默认情况下,我们在Kibana中查看的时间字段与日志信息中的时间不一致,因为默认的时间字段值是日志收集时的当前时间,所以需要将该字段的时间替换为日志信息中的时间。解决方案:使用grok分词插件与date时间格式化插件来实现在Logstash的配置文件的过滤器中配置grok分词插件与date时间格式化插件,如:input {beats {port => 5044}}filter {multiline {pattern => "%{LOGLEVEL}s*][%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME}]" negate => true what => "previous"}grok {match => [ "message" , "(?%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME})" ]}date {match => ["customer_time", "yyyyMMdd HH:mm:ss,SSS"] //格式化时间target => "@timestamp"//替换默认的时间字段}}output { elasticsearch{hosts => "localhost:9200"}}如要匹配的日志格式为:“[DEBUG][20170811 10:07:31,359][DefaultBeanDefinitionDocumentReader:106] Loading bean definitions”,解析出该日志的时间字段的方式有:① 通过引入写好的表达式文件,如表达式文件为customer_patterns,内容为: CUSTOMER_TIME %{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME}注:内容格式为:[自定义表达式名称] [正则表达式] 然后logstash中就可以这样引用:filter {grok {patterns_dir => ["./customer-patterms/mypatterns"] //引用表达式文件路径match => [ "message" , "%{CUSTOMER_TIME:customer_time}" ] //使用自定义的grok表达式 }}123456② 以配置项的方式,规则为:(?<自定义表达式名称>正则匹配规则),如:filter {grok {match => [ "message" , "(?%{YEAR}%{MONTHNUM}%{MONTHDAY}s+%{TIME})" ] }}12345问题:如何在Kibana中通过选择不同的系统日志模块来查看数据一般在Kibana中显示的日志数据混合了来自不同系统模块的数据,那么如何来选择或者过滤只查看指定的系统模块的日志数据?解决方案:新增标识不同系统模块的字段或根据不同系统模块建ES索引1、新增标识不同系统模块的字段,然后在Kibana中可以根据该字段来过滤查询不同模块的数据 这里以第二种部署架构讲解,在Filebeat中的配置内容为:filebeat.prospectors:-paths:
  • /home/project/elk/logs/account.log
input_type: logmultiline:pattern: '^['negate: truematch: afterfields: //新增log_from字段log_from: account-paths:
  • /home/project/elk/logs/customer.log
input_type: logmultiline:pattern: '^['negate: truematch: afterfields:log_from: customeroutput:logstash:hosts: ["localhost:5044"]通过新增:log_from字段来标识不同的系统模块日志2、根据不同的系统模块配置对应的ES索引,然后在Kibana中创建对应的索引模式匹配,即可在页面通过索引模式下拉框选择不同的系统模块数据。 这里以第二种部署架构讲解,分为两步: ① 在Filebeat中的配置内容为:filebeat.prospectors:-paths:
  • /home/project/elk/logs/account.log
input_type: logmultiline:pattern: '^['negate: truematch: afterdocument_type: account-paths:
  • /home/project/elk/logs/customer.log

input_type: log

multiline:

pattern: '^['

negate: truematch: after

document_type: customeroutput: logstash:

hosts: ["localhost:5044"]

通过document_type来标识不同系统模块

② 修改Logstash中output的配置内容为:

output {

elasticsearch {

hosts => "localhost:9200"

index => "%{type}"

} }123

在output中增加index属性,%{type}表示按不同的document_type值建ES索引

四、总结

本文主要介绍了ELK实时日志分析的三种部署架构,以及不同架构所能解决的问题,这三种架构中第二种部署方式是时下最流行也是最常用的部署方式,最后介绍了ELK作在日志分析中的一些问题与解决方案,说在最后,ELK不仅仅可以用来作为分布式日志数据集中式查询和管理,还可以用来作为项目应用以及服务器资源监控等场景。

使用Spring Cloud和Docker构建微服务架构

李颖杰 发表了文章 • 0 个评论 • 20841 次浏览 • 2017-10-07 21:49 • 来自相关话题

【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式 ...查看全部
【编者的话】如何使用Spring Boot、Spring Cloud、Docker和Netflix的一些开源工具来构建一个微服务架构。本文通过使用Spring Boot、Spring Cloud和Docker构建的概念型应用示例,提供了了解常见的微服务架构模式的起点。

该代码可以在GitHub上获得,并且在Docker Hub上提供了镜像。您只需要一个命令即可启动整个系统。

我选择了一个老项目作为这个系统的基础,它的后端以前是单一应用。此应用提供了处理个人财务、整理收入开销、管理储蓄、分析统计和创建简单预测等功能。
#功能服务
整个应用分解为三个核心微服务。它们都是可以独立部署的应用,围绕着某些业务功能进行组织。
1.png


账户服务

包含一般用户输入逻辑和验证:收入/开销记录、储蓄和账户设置。
biao1.png


统计服务

计算主要的统计参数,并捕获每一个账户的时间序列。数据点包含基于货币和时间段正常化后的值。该数据可用于跟踪账户生命周期中的现金流量动态。
biao2.png


通知服务

存储用户的联系信息和通知设置(如提醒和备份频率)。安排工作人员从其它服务收集所需的信息并向订阅的客户发送电子邮件。
biao3.png


注意

* 每一个微服务拥有自己的数据库,因此没有办法绕过API直接访问持久数据。
* 在这个项目中,我使用MongoDB作为每一个服务的主数据库。拥有一个多种类持久化架构(polyglot persistence architecture)也是很有意义的。
* 服务间(Service-to-service)通信是非常简单的:微服务仅使用同步的REST API进行通信。现实中的系统的常见做法是使用互动风格的组合。例如,执行同步的GET请求检索数据,并通过消息代理(broker)使用异步方法执行创建/更新操作,以便解除服务和缓冲消息之间的耦合。然而,这带给我们是最终的一致性

#基础设施服务
分布式系统中常见的模式,可以帮助我们描述核心服务是怎样工作的。Spring Cloud提供了强大的工具,可以增强Spring Boot应用的行为来实现这些模式。我会简要介绍一下:
2.png

##配置服务
Spring Cloud Config是分布式系统的水平扩展集中式配置服务。它使用了当前支持的本地存储、Git和Subversion等可拔插存储库层(repository layer)。

在此项目中,我使用了native profile,它简单地从本地classpath下加载配置文件。您可以在配置服务资源中查看shared目录。现在,当通知服务请求它的配置时,配置服务将响应回shared/notification-service.yml和shared/application.yml(所有客户端应用之间共享)。

客户端使用

只需要使用sprng-cloud-starter-config依赖构建Spring Boot应用,自动配置将会完成其它工作。

现在您的应用中不需要任何嵌入的properties,只需要提供有应用名称和配置服务url的bootstrap.yml即可:
spring:
application:
name: notification-service
cloud:
config:
uri: http://config:8888
fail-fast: true

使用Spring Cloud Config,您可以动态更改应用配置

比如,EmailService bean使用了@RefreshScope注解。这意味着您可以更改电子邮件的内容和主题,而无需重新构建和重启通知服务应用。

首先,在配置服务器中更改必要的属性。然后,对通知服务执行刷新请求:curl -H "Authorization: Bearer #token#" -XPOST http://127.0.0.1:8000/notifications/refresh。

您也可以使用webhook来自动执行此过程

注意

* 动态刷新存在一些限制。@RefreshScope不能和@Configuraion类一同工作,并且不会作用于@Scheduled方法。
* fail-fast属性意味着如果Spring Boot应用无法连接到配置服务,将会立即启动失败。当您一起启动所有应用时,这非常有用。
* 下面有重要的安全提示

##授权服务
负责授权的部分被完全提取到单独的服务器,它为后端资源服务提供OAuth2令牌。授权服务器用于用户授权以及在周边内进行安全的机器间通信。

在此项目中,我使用密码凭据作为用户授权的授权类型(因为它仅由本地应用UI使用)和客户端凭据作为微服务授权的授权类型。

Spring Cloud Security提供了方便的注解和自动配置,使其在服务器端或者客户端都可以很容易地实现。您可以在文档中了解到更多信息,并在授权服务器代码中检查配置明细。

从客户端来看,一切都与传统的基于会话的授权完全相同。您可以从请求中检索Principal对象、检查用户角色和其它基于表达式访问控制和@PreAuthorize注解的内容。

PiggyMetrics(帐户服务、统计服务、通知服务和浏览器)中的每一个客户端都有一个范围:用于后台服务的服务器、用于浏览器展示的UI。所以我们也可以保护控制器避免受到外部访问,例如:
@PreAuthorize("#oauth2.hasScope('server')")
@RequestMapping(value = "accounts/{name}", method = RequestMethod.GET)
public List getStatisticsByAccountName(@PathVariable String name) {
return statisticsService.findByAccountName(name);
}

##API网关
您可以看到,有三个核心服务。它们向客户端暴露外部API。在现实系统中,这个数量可以非常快速地增长,同时整个系统将变得非常复杂。实际上,一个复杂页面的渲染可能涉及到数百个服务。

理论上,客户端可以直接向每个微服务直接发送请求。但是这种方式是存在挑战和限制的,如果需要知道所有端点的地址,分别对每一段信息执行http请求,将结果合并到客户端。另一个问题是,这不是web友好协议,可能只在后端使用。

通常一个更好的方法是使用API网关。它是系统的单个入口点,用于通过将请求路由到适当的后端服务或者通过调用多个后端服务并聚合结果来处理请求。此外,它还可以用于认证、insights、压力测试、金丝雀测试(canary testing)、服务迁移、静态响应处理和主动变换管理。

Netflix开源这样的边缘服务,现在用Spring Cloud,我们可以用一个@EnabledZuulProxy注解来启用它。在这个项目中,我使用Zuul存储静态内容(UI应用),并将请求路由到适当的微服务。以下是一个简单的基于前缀(prefix-based)路由的通知服务配置:
zuul:
routes:
notification-service:
path: /notifications/**
serviceId: notification-service
stripPrefix: false

这意味着所有以/notification开头的请求将被路由到通知服务。您可以看到,里面没有硬编码的地址。Zuul使用服务发现机制来定位通知服务实例以及断路器和负载均衡器,如下所述。
##服务发现
另一种常见的架构模式是服务发现。它允许自动检测服务实例的网络位置,由于自动扩展、故障和升级,它可能会动态分配地址。

服务发现的关键部分是注册。我使用Netflix Eureka进行这个项目,当客户端需要负责确定可以用的服务实例(使用注册服务器)的位置和跨平台的负载均衡请求时,Eureka就是客户端发现模式的一个很好的例子。

使用Spring Boot,您可以使用spring-cloud-starter-eureka-server依赖、@EnabledEurekaServer注解和简单的配置属性轻松构建Eureka注册中心(Eureka Registry)。

使用@EnabledDiscoveryClient注解和带有应用名称的bootstrap.yml来启用客户端支持:
spring:
application:
name: notification-service

现在,在应用启动时,它将向Eureka服务器注册并提供元数据,如主机和端口、健康指示器URL、主页等。Eureka接收来自从属于某服务的每个实例的心跳消息。如果心跳失败超过配置的时间表,该实例将从注册表中删除。

此外,Eureka还提供了一个简单的界面,您可以通过它来跟踪运行中的服务和可用实例的数量:http://localhost:8761
3.png

##负载均衡器、断路器和Http客户端
Netflix OSS提供了另一套很棒的工具。

Ribbon

Ribbon是一个客户端负载均衡器,可以很好地控制HTTP和TCP客户端的行为。与传统的负载均衡器相比,每次线上调用都不需要额外的跳跃——您可以直接联系所需的服务。

它与Spring Cloud和服务发现是集成在一起的,可开箱即用。Eureka客户端提供了可用服务器的动态列表,因此Ribbon可以在它们之间进行平衡。

Hystrix

Hystrix是断路器模式的一种实现,它可以通过网络访问依赖来控制延迟和故障。中心思想是在具有大量微服务的分布式环境中停止级联故障。这有助于快速失败并尽快恢复——自我修复在容错系统中是非常重要的。

除了断路器控制,在使用Hystrix,您可以添加一个备用方法,在主命令失败的情况下,该方法将被调用以获取默认值。

此外,Hystrix生成每个命令的执行结果和延迟的度量,我们可以用它来监视系统的行为

Feign

Feign是一个声明式HTTP客户端,能与Ribbon和Hystrix无缝集成。实际上,通过一个spring-cloud-starter-feign依赖和@EnabledFeignClients注解,您可以使用一整套负载均衡器、断路器和HTTP客户端,并附带一个合理的的默认配置。

以下是账户服务的示例:
@FeignClient(name = "statistics-service")
public interface StatisticsServiceClient {
@RequestMapping(method = RequestMethod.PUT, value = "/statistics/{accountName}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
void updateStatistics(@PathVariable("accountName") String accountName, Account account);
}


* 您需要的只是一个接口
* 您可以在Spring MVC控制器和Feign方法之间共享@RequestMapping部分
* 以上示例仅指定所需要的服务ID——statistics-service,这得益于Eureka的自动发现(但显然您可以使用特定的URL访问任何资源)。

##监控仪表盘
在这个项目配置中,Hystrix的每一个微服务都通过Spring Cloud Bus(通过AMQP broker)将指标推送到Turbine。监控项目只是一个使用了TurbineHystrix仪表盘的小型Spring Boot应用。

让我们看看系统行为在负载下:账户服务调用统计服务和它在一个变化的模拟延迟下的响应。响应超时阈值设置为1秒。
4.png

##日志分析
集中式日志记录在尝试查找分布式环境中的问题时非常有用。Elasticsearch、Logstash和Kibana技术栈可让您轻松搜索和分析您的日志、利用率和网络活动数据。在我的另一个项目中已经有现成的Docker配置。
##安全

高级安全配置已经超过了此概念性项目的范围。为了更真实地模拟真实系统,请考虑使用https和JCE密钥库来加密微服务密码和配置服务器的properties内容(有关详细信息,请参阅文档)。
#基础设施自动化

部署微服务比部署单一的应用的流程要复杂得多,因为它们相互依赖。拥有完全基础设置自动化是非常重要的。我们可以通过持续交付的方式获得以下好处:

* 随时发布软件的能力。
* 任何构建都可能最终成为一个发行版本。
* 构建工件(artifact)一次,根据需要进行部署。

这是一个简单的持续交付工作流程,在这个项目的实现:

在此配置中,Travis CI为每一个成功的Git推送创建了标记镜像。因此,每一个微服务在Docker Hub上的都会有一个latest镜像,而较旧的镜像则使用Git提交的哈希进行标记。如果有需要,可以轻松部署任何一个,并快速回滚。
5.png

#如何运行全部?
这真的很简单,我建议您尝试一下。请记住,您将要启动8个Spring Boot应用、4个MongoDB实例和RabbitMq。确保您的机器上有4GB的内存。您可以随时通过网关、注册中心、配置、认证服务和账户中心运行重要的服务。

运行之前

* 安装Docker和Docker Compose。
* 配置环境变量:CONFIG_SERVICE_PASSWORD, NOTIFICATION_SERVICE_PASSWORD, STATISTICS_SERVICE_PASSWORD, ACCOUNT_SERVICE_PASSWORD, MONGODB_PASSWORD

生产模式

在这种模式下,所有最新的镜像都将从Docker Hub上拉取。只需要复制docker-compose.yml并执行docker-compose up -d即可。

开发模式

如果您想自己构建镜像(例如,在代码中进行一些修改),您需要克隆所有仓库(repository)并使用Mavne构建工件(artifact)。然后,运行docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

docker-compose.dev.yml继承了docker-compose.yml,附带额外配置,可在本地构建镜像,并暴露所有容器端口以方便开发。

重要的端点(Endpoint)

* localhost:80 —— 网关
* localhost:8761 —— Eureka仪表盘
* localhost:9000 —— Hystrix仪表盘
* localhost:8989 —— Turbine stream(Hystrix仪表盘来源)
* localhost:15672 —— RabbitMq管理

注意

所有Spring Boot应用都需要运行配置服务器才能启动。得益于Spring Boot的fail-fast属性和docker-compsoe的restart:always选项,我们可以同时启动所有容器。这意味着所有依赖的容器将尝试重新启动,直到配置服务器启动运行为止。

此外,服务发现机制在所有应用启动后需要一段时间。在实例、Eureka服务器和客户端在其本地缓存中都具有相同的元数据之前,任何服务都不可用于客户端发现,因此可能需要3次心跳。默认的心跳周期为30秒。

原文链接:Microservice Architectures With Spring Cloud and Docker(翻译:Oopsguy

秦苍科技是如何管理数百个微服务并避免踩坑的?

尼古拉斯 发表了文章 • 0 个评论 • 2567 次浏览 • 2017-09-25 20:30 • 来自相关话题

【编者的话】过去两年中,微服务架构是一个非常热门的技术名词。秦苍科技也在微服务方面做了大量的投资和实践,我们有开发、测试、准生产、生产四套环境,每套环境有230+个微服务,总共有近1000个微服务。 秦苍科技为什么要采用微服务的架构? ...查看全部
【编者的话】过去两年中,微服务架构是一个非常热门的技术名词。秦苍科技也在微服务方面做了大量的投资和实践,我们有开发、测试、准生产、生产四套环境,每套环境有230+个微服务,总共有近1000个微服务。

秦苍科技为什么要采用微服务的架构?如何管理这么多微服务?本文将对这些问题进行阐述,希望对正在踩坑路上和即将踩坑的朋友们有所帮助。
#为什么要使用微服务
关于微服务架构优点有很多讨论。但是,个人认为许多优点都可以算作一些“伪优点”。例如:

* 从单个服务的角度而言,微服务的每个服务都很简单,只关注于一个业务功能,降低了单个服务的复杂性。但是,从整体而言,作为一种分布式系统,微服务引入额外的复杂性和问题,比如说网络延迟、容错性、异步、分布式事务等。
* 从单个服务的角度而言,每个微服务可以通过不同的编程语言与工具进行开发,针对不同的服务采用更加合适的技术,也可以快速地尝试一些新技术。
* 但是,从整个公司的角度来说,往往希望能够尽量统一技术栈,避免重复投资某些技术。假设某公司主要用Spring Boot和Spring Cloud来构建微服务,使用Netflix Hystrix作为服务熔断的解决方案。后来,一些微服务开始使用Node.js来实现。很快,该公司就发觉使用Node.js构建的服务无法使用已有的服务熔断解决方案,需要为Node.js技术栈重新开发。
* ……

我的观点是微服务架构的核心就是解决扩展性的问题。从组织结构的角度来看,微服务架构使得研发部门可以快速扩张,因为每个微服务都不是特别复杂,工作在这个服务上的研发人员不是必须对整个系统都充分了解,很多新人可以快速上手。

从技术的角度来看,微服务架构使得每个微服务可以独立部署、独立扩展,可以根据每个服务的规模来部署满足需求的规模,选择更适合于服务资源需求的硬件。

秦苍科技正处在一个人员规模和业务规模快速扩张的阶段,微服务的扩展性非常贴切地满足了我们现阶段的需求,所以使用微服务架构对秦苍科技来说也变成了一件顺理成章的事情了。
#如何进行服务管理
随着服务数量的增多,我们发觉微服务间的依赖关系越来越复杂,一个服务的改变将会波及多个服务,错误排查也相当困难。当系统有几百个服务时,这种依赖简直就是一个噩梦。

所以,秦苍科技启动了服务治理的项目,使用服务注册和发现技术简化服务的管理,对服务进行了分组、分层,降低系统的复杂性和耦合性。

其实,服务的管理和人员组织结构的管理非常类似。当一个组织中成员增多时,我们会将人员分为若干个小的团队,每个团队由较少的人员组成,负责某个比较独立的业务,并且会有一个团队负责人负责和其他团队的沟通。

当组织中的成员进一步增多时,我们会将若干个团队合并为一个部门,每个部门负责某个独立的职能。

对于微服务的管理,我们采用与组织结构管理类似的方法,把彼此紧密相关的服务构建成逻辑上的一个组。类似于组织结构中的团队负责人,该组有一个API网关,向外暴露了组中所有服务的功能。对于该组中服务的使用方来说,都通过这个API网关进行访问,仿佛这个组就是一个服务一样,无需关心该组是由多少个服务组成。

通过分组的方式,秦苍230+个微服务变为了25个组,从而大大降低了系统逻辑上的复杂性。然后,我们把系统分为了若干层,每一层由若干个组组成。上层只可以调用下层的服务,下层不可以调用上层服务。通过分层的方式,我们降低了系统的耦合性。
01.jpg

图 1 服务分层的组织方式
#如何让服务管理自动化?
在人员组织结构管理中,为了高效地管理人员的信息,通常会引入一套系统管理这些信息,例如:微软的Active Directory。

当一个新员工入职的时候,我们会在这个系统中添加该成员的基本信息,例如:姓名、电话、email等信息。

在一个员工离职的时候,我们会在这个系统中删除该员工。当一个员工A要和员工B沟通交流的时候,员工A可以根据员工B的姓名在这个系统中查询出员工B的电话、email等信息,然后使用电话或email和员工B进行沟通。

对于微服务的管理,我们也希望有这样一个系统,能够注册和查询微服务的所有信息。微服务架构中把这个系统叫做服务注册中心。

秦苍科技采用了Netflix Eureka作为我们的服务注册中心,所有的微服务都基于Spring Boot和Spring Cloud进行构建。

系统中的每个服务都非常“聪明”,在启动后都会跑到服务注册中心“自报家门”,告诉服务注册中心自己的名字、IP地址、版本、状态、所属组、所属层、所属层的级别、依赖的微服务等信息,服务注册中心会将这些服务保存到它的“花名册”上。

通过服务注册中心的“花名册”,我们可以对系统一览无遗,轻松了解系统的每一层,每一层中所有组,每个组中所有服务的信息。

当服务A依赖于服务B时,它只要知道服务B的名称,就可以从服务注册中心的“花名册”中查询到服务B的所有实例,及其相关的所有信息,例如IP地址、所属组、所属层等信息。

这样,服务A不再需要“死记硬背”服务B的IP地址。当服务A调用服务B的时候,服务A首先从服务注册中心获取到服务B的所有实例,然后服务A采用某种策略从服务B的实例中选择其中一个实例将请求发送给该实例,从而实现了客户端负载均衡。

这个客户端负载均衡的功能就是由图2中的Netflix Ribbon模块来完成的。
02.jpg

图 2 服务注册和发现机制
#如何自动控制服务依赖?
使用前述的方法后,我们可以使用分组分层的方式对服务进行管理。但是,我们仍然需要一定的技术手段来保证开发人员在开发某个微服务的时候一定会遵守下层服务不能调用上层服务的原则,保证开发人员不会引入不该引入的微服务依赖。

秦苍实现自动控制服务依赖的核心是注册服务依赖信息到服务注册中心,扩展Netflix Ribbon限制服务调用。

使用前述的方法后,我们可以使用分组分层的方式对服务进行管理。但是,我们仍然需要一定的技术手段来保证开发人员在开发某个微服务的时候一定会遵守下层服务不能调用上层服务的原则,保证开发人员不会引入不该引入的微服务依赖。

秦苍实现自动控制服务依赖的核心是注册服务依赖信息到服务注册中心,扩展Netflix Ribbon限制服务调用。
##服务注册依赖信息到注册中心
每个微服务都会注册该服务所属层、所属层级、依赖的微服务等信息到服务注册中心。这些服务依赖信息作为服务的配置项,保存在配置文件中,统一由运维人员管理。开发人员在开发环境中可以修改这些服务依赖信息,进行开发调试。

但是,在测试、准生产和生产环境中,运维人员会使用自己管理的配置项覆盖掉这些信息,运维只有在经过架构师同意后才会修改这些服务依赖信息。

这就意味着开发无法绕过架构师自行引入新的依赖,否则在测试、准生产和生产环境中服务是调不通的,代码无法正常工作,这样就从技术手段上保证了无法随意地引入微服务依赖。
##扩展Netflix Ribbon限制服务调用
有了服务依赖信息后,服务调用时我们需要使用这些信息限制不允许的服务调用。只要对Ribbon进行少许扩展就可以满足这样的需求。

本质来讲,Ribbon就是一个服务调用的“路由器”。只要在这个“路由器”上定义一些新的规则,我们就可以控制服务的调用关系。例如:

白名单规则:对于一个微服务,只能调用在该服务白名单列表中的服务,否则调用失败。

层级调用规则:对于一个微服务,只能调用层级比自己低的服务,否则调用失败。

……

目前,秦苍科技对Spring Boot Admin进行了扩展来构建自己的服务治理中心,用户可以按照组的方式浏览服务,查看每个服务的健康状态、配置信息、日志等。
03.png

图 3 服务治理中心

将来,我们打算通过读取服务注册中心中每个服务注册的元信息,在服务治理中心中自动画出整个系统的架构图。关于自动管控服务依赖方面,我们的工作还在进行中。

希望将来有一天,我们在微服务治理方面的积累足够成熟,可以将这些经验回馈给开源社区。

作者介绍:李荣陆,秦苍科技高级技术总监兼首席架构师。复旦大学计算机博士。曾在Autodesk、SAP、Blackboard等公司担任首席工程师、架构师、高级经理等职位。在国内、国际权威杂志和会议上发表十余篇论文,出版过一本著作。十几年来在云计算、计算机辅助设计、自然语言处理、搜索引擎、数据挖掘、人工智能、机器学习、微服务架构等领域均有涉猎。

原文链接:如何管理数百个微服务并避免踩坑?

Spring Cloud在国内中小型公司能用起来吗?

尼古拉斯 发表了文章 • 0 个评论 • 2958 次浏览 • 2017-09-12 19:25 • 来自相关话题

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring B ...查看全部
今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题『Spring Cloud在国内中小型公司能用起来吗?』,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring Boot和Spring Cloud近两年的程序员,看的我手痒痒不答不快呀。
#好问题
好问题必须配认真的回答,仔细的看了题主的问题,发现这个问题非常具有代表性,可能是广大网友想使用Spring Cloud却又对Spring Cloud不太了解的共同想法,题主对Spring Cloud使用的方方面面都进行过了思考,包括市场,学习、前后端、测试、配置、部署、开发以及运维,下面就是题主原本的问题:

想在公司推广Spring Cloud,但我对这项技术还缺乏了解,画了一张脑图,总结了种种问题。

01.jpg

微服务是这样一个结构吗:



前端或二方 - > ng集群 -> zuul集群 -> eureka-server集群 -> service provider集群
(二方指其他业务部门)



想要明白这个问题,首先需要知道什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系?
#什么是Spring Boot?
Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只需要很少的Spring配置。

Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架(不知道这样比喻是否合适)。

Spring Boot的核心思想就是约定大于配置,一切自动完成。采用Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。如果你对Spring Boot完全不了解,可以参考我的这篇文章:《SpringBoot(一):入门篇-%E5%85%A5%E9%97%A8%E7%AF%87.html)》。
#什么是Spring Cloud?
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud做为大管家就需要提供各种方案来维护整个生态。

Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能,如果你对Spring Cloud组件不是特别了解的话,可以参考我的这篇文章:《Spring Cloud(一):大话Spring Cloud》。
#Spring Boot和Spring Cloud的关系
Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。

Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。

Spring -> Spring Boot > Spring Cloud 这样的关系。
#回答

以下为我在知乎的回答。

首先楼主问的这些问题都挺好的,算是经过了自己的一番思考,我恰好经历了你所说的中小公司,且都使用Spring Cloud并且已经投产上线。第一家公司技术开发人员15人左右,项目实例 30多,第二家公司开发人员100人左右,项目实例达160多。

实话说Spring Boot、Spring Cloud仍在高速发展,技术生态不断的完善和扩张,不免也会有一些小的bug,但对于中小公司的使用来将,完全可以忽略,基本都可以找到解决方案,接下来回到你的问题。
##1、市场
据我所知有很多知名互联网公司都已经使用了Spring Cloud,比如阿里、美团但都是小规模,没有像我经历的这俩家公司,业务线全部拥抱Spring Cloud;另外Spring Cloud并不是一套高深的技术,普通的Java程序员经过一到俩个月完全就可以上手,但前期需要一个比较精通人的来带队。
##2、学习
有很多种方式,现在Spring Cloud越来越火的情况下,各种资源也越来越丰富,查看官方文档和示例,现在很多优秀的博客在写spirng cloud的相关教程,我这里收集了一些Spring Boot和Spring Cloud的相关资源可以参考,找到博客也就找到人和组织了。
##3、前后职责划分
其实这个问题是每个系统架构都应该考虑的问题,Spring Cloud只是后端服务治理的一套框架,唯一和前端有关系的是thymeleaf,Spring推荐使用它做模板引擎。一般情况下,前端App或者网页通过Zuul来调用后端的服务,如果包含静态资源也可以使用Nginx做一下代理转发。
##4、测试
Spring-boot-starter-test支持项目中各层方法的测试,也支持controller层的各种属性。所以一般测试的步奏是这样,首先开发人员覆盖自己的所有方法,然后测试微服务内所有对外接口保证微服务内的正确性,再进行微服务之间集成测试,最后交付测试。
##5、配置
session共享有很多种方式,比如使用tomcat sesion共享机制,但我比较推荐使用Redis缓存来做session共享。完全可以分批引入,我在上一家公司就是分批过渡上线,新旧项目通过Zuul进行交互,分批引入的时候,最好是新业务线先使用Spring Cloud,老业务做过渡,当完全掌握之后在全部替换。如果只是请求转发,Zuul的性能不一定比Nginx低,但是如果涉及到静态资源,还是建议在前端使用Nginx做一下代理。另外Spring Cloud有配置中心,可以非常灵活的做所有配置的事情。
##6、部署
多环境不同配置,Spring Boot最擅长做这个事情了,使用不同的配置文件来配置不同环境的参数,在服务启动的时候指明某个配置文件即可,例如:java -jar app.jar --spring.profiles.active=dev就是启动测试环境的配置文件;Spring Cloud 没有提供发布平台,因为Jenkins已经足够完善了,推荐使用jenkins来部署Spring Boot项目,会省非常多的事情;灰度暂时不支持,可能需要自己来做,如果有多个实例,可以一个一个来更新;支持混合部署,一台机子部署多个是常见的事情。
##7、开发
你说的包含html接口就是前端页面吧,Spring Boot可以支持,但其实也是Spring Mvc在做这个事情,Spring Cloud只做服务治理,其它具体的功能都是集成了各种框架来解决而已;excel报表可以,其实除过swing项目外,其它Java项目都可以想象;Spring Cloud和老项目可以混合使用,通过Zuul来支持。是否支持callback,可以通过MQ来实现,还是强调Spring Cloud只是服务治理。
##8、运维
Turbine、Zipkin可以用来做熔断和性能监控;动态上下线某个节点可以通过Jenkins来实现;Provider下线后,会有其它相同的实例来提供服务,Eureka会间隔一段时间来检测服务的可用性;不同节点配置不同的流量权值目前还不支持。注册中心必须做高可用集群,注册中心挂掉之后,服务实例会全部停止。

总结,中小企业是否能用的起来Spring Cloud,完全取决于自己公司的环境,如果是一个技术活跃型的团队就大胆的去尝试吧,目前Spring Cloud是所有微服务治理中最优秀的方案,也是一个趋势,未来一两年可能就会像Spring一样流行,早接触早学习岂不更好。

希望能解答了你的疑问。
#Spring Cloud 架构
我们从整体来看一下Spring Cloud主要的组件,以及它的访问流程:
02.jpg


* 外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务
* 网关接收到请求后,从注册中心(Eureka)获取可用服务
* 由Ribbon进行均衡负载后,分发到后端的具体实例
* 微服务之间通过Feign进行通信处理业务
* Hystrix负责处理服务超时熔断
* Turbine监控服务间的调用和熔断相关指标

图中没有画出配置中心,配置中心管理各微服务不同环境下的配置文件。

以上就是一个完整的Spring Cloud生态图。

最后送一个完整示例的Spirng Cloud开源项目等你去拿:https://github.com/ityouknow/spring-cloud-examples

原文链接:https://juejin.im/post/59b72f865188257e8153732d

Docker with Spring Boot

nkduqi 发表了文章 • 2 个评论 • 5659 次浏览 • 2016-03-06 15:27 • 来自相关话题

前段时间在我厂卷爷的指导下将Docker在我的实际项目中落地,最近几个小demo都尽量熟悉docker的使用,希望通过这篇文章分享我截止目前的使用经验(如有不准确的表述,欢迎帮我指出)。本文的主要内容是关于Java应用程序的docker化,首先简单介绍了doc ...查看全部
前段时间在我厂卷爷的指导下将Docker在我的实际项目中落地,最近几个小demo都尽量熟悉docker的使用,希望通过这篇文章分享我截止目前的使用经验(如有不准确的表述,欢迎帮我指出)。本文的主要内容是关于Java应用程序的docker化,首先简单介绍了docker和docker-compose,然后利用两个案例进行实践说明。

简单说说Docker,现在云计算领域火得一塌糊涂的就是它了吧。Docker的出现是为了解决PaaS的问题:运行环境与具体的语言版本、项目路径强关联,因此干脆利用lxc技术进行资源隔离,构造出跟随应用发布的运行环境,这样就解决了语言版本的限制问题。PaaS的出现是为了让运维人员不需要管理一台虚拟机,IaaS的出现是为了让运维人员不需要管理物理机。云计算,说到底都是俩字——运维。

云计算领域的技术分为虚拟化技术和资源管理两个方面,正好对应我们今天要讲的两个工具:Docker和docker-compose。Docker的主要概念有:容器、镜像、仓库;docker-compose是fig的后续版本,负责将多个docker服务整合起来,对外提供一致服务。

1. Spring Boot应用的docker化
首先看Spring Boot应用程序的docker化,由于Spring Boot内嵌了tomcat、Jetty等容器,因此我们对docker镜像的要求就是需要java运行环境。我的应用代码的的Dockerfile文件如下:

```
#基础镜像:仓库是java,标签用8u66-jdk
FROM java:8u66-jdk
#当前镜像的维护者和联系方式
MAINTAINER duqi duqi@example.com
#将打包好的spring程序拷贝到容器中的指定位置
ADD target/bookpub-0.0.1-SNAPSHOT.jar /opt/bookpub-0.0.1-SNAPSHOT.jar
#容器对外暴露8080端口
EXPOSE 8080
#容器启动后需要执行的命令
CMD java -Djava.security.egd=file:/dev/./urandom -jar /opt/bookpub-0.0.1-SNAPSHOT.jar
```
因为目前的示例程序比较简单,这个dockerfile并没有在将应用程序的数据存放在宿主机上。如果你的应用程序需要写文件系统,例如日志,最好利用`VOLUME /tmp`命令,这个命令的效果是:在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录。

把这个Dockerfile放在项目的根目录下即可,后续通过`docker-compose build`统一构建:基础镜像是只读的,然后会在该基础镜像上增加新的可写层来供我们使用,因此java镜像只需要下载一次。

docker-compose是用来做docker服务编排,参看《Docker从入门到实践》中的解释:

Compose 项目目前在 Github 上进行维护,目前最新版本是 1.2.0。Compose 定位是“defining and running complex applications with Docker”,前身是 Fig,兼容 Fig 的模板文件。



Dockerfile 可以让用户管理一个单独的应用容器;而 Compose 则允许用户在一个模板(YAML 格式)中定义一组相关联的应用容器(被称为一个 project,即项目),例如一个 Web 服务容器再加上后端的数据库服务容器等。



单个docker用起来确实没什么用,docker技术的关键在于持续交付,通过与jekins的结合,可以实现这样的效果:开发人员提交push,然后jekins就自动构建并测试刚提交的代码,这就是我理解的持续交付。

2. spring boot + redis + mongodb
在这个项目中,我启动三个容器:web、redis和mongodb,然后将web与redis连接,web与mongodb连接。首先要进行redis和mongodb的docker化,redis镜像的Dockerfile内容是:

```
FROM ubuntu:14.04
RUN apt-get update
RUN apt-get -y install redis-server
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
```
Mongodb镜像的Dockerfile内容是,docker官方给了mongodb的docker化教程,我直接拿来用了,参见Dockerizing MongoDB

```
# Format: FROM repository[:version]
FROM ubuntu:14.04
# Format: MAINTAINER Name
MAINTAINER duqi duqi@example.com
# Installation:
# Import MongoDB public GPG key AND create a MongoDB list file
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
RUN echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list
# Update apt-get sources AND install MongoDB
RUN apt-get update && apt-get install -y mongodb-org
# Create the MongoDB data directory
RUN mkdir -p /data/db
# Expose port 27017 from the container to the host
EXPOSE 27017
# Set usr/bin/mongod as the dockerized entry-point application
ENTRYPOINT ["/usr/bin/mongod"]```


使用docker-compose编排三个服务,具体的模板文件如下:
```
web:
build: .
ports:
- "49161:8080"
links:
- redis
- mongodb
redis:
image: duqi/redis
ports:
- "6379:6379"
mongodb:
image: duqi/mongodb
ports:
- "27017:27017"
```

架构比较简单,第一个区块的build,表示docker中的命令“docker build .”,用于构建web镜像;ports这块表示将容器的8080端口与宿主机(IP地址是:192.168.99.100)的49161对应。因为现在docker不支持原生的osx,因此在mac下使用docker,实际上是在mac上的一台虚拟机(docker-machine)上使用docker,这台机器的地址就是192.168.99.100。参见:在mac下使用docker

links表示要连接的服务,redis与下方的redis区块对应、mongodb与下方的mongodb区块对应。redis和mongodb类似,首先说明要使用的镜像,然后规定端口映射。

那么,如何运行呢?
  1. 命令`docker-compose build`,表示构建web服务,目前我用得比较土,就是编译jar之后还需要重新更新docker,优雅点不应该这样。

  1. 命令`docker-compose up`,表示启动web服务,可以看到mongodb、redis和web依次启动,启动后用`docker ps`查看当前的运行容器。


特别注意,在配置文件中写redis和mongodb的url时,要用虚拟机的地址,即192.168.99.100。例如,redis的一个配置应该为:spring.redis.host=192.168.99.100。

3. spring boot + mysql
拉取mysql镜像的指令是:`docker run --name db001 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin -d mysql:5.7`,表示启动的docker容器名字是db001,登录密码一定要设定, -d表示设置Mysql版本。

我的docker-compose模板文件是:

```
web:
build: .
ports:
- "49161:8080"
links:
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: springbootcookbook
ports:
- "3306:3306"
```

主要内容跟之前的类似,主要讲下mysql部分,通过environement来设置进入mysql容器后的环境变量,即连接数据库的密码MYSQL_ROOT_PASSWORD,使用的数据库名称MSYQL_DATABASE等等。

一直想写这篇文章做个总结,写来发现还是有点薄,对于docker我还需要系统得学习,不过,针对上面的例子,我都是亲自实践过的,大家有什么问题可以与我联系。

参考资料
  1. Docker从入门到实践
  2. Docker - Initialize mysql database with schema
  3. 使用Docker搭建基础的mysql应用
  4. Spring Boot with docker

----
作者介绍
杜琪,现就职于阿里巴巴,研发工程师,负责Yunos系统云服务-PMS(个人移动服务),目前的研究方向是Spring Boot、微服务、Docker等技术。热爱写作,倡导写作驱动学习,个人技术博客地址:link

Spring Boot与Docker(四):额外的微服务、更新容器、Docker Compose和负载均衡

国会山上的猫TuxHu 发表了文章 • 0 个评论 • 7227 次浏览 • 2015-12-15 23:11 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adba ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第四篇,本篇我们我们将添加一些额外的服务/容器,并且更新容器,采用Docker Compose以及使用HAProxy容器进行负载均衡。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治●华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

现在我们对于微服务和Docker有了扎实的了解,启动了一个MongoDB容器和Spring Boot微服务容器并且借助于容器的link机制(参考我们的Git版本库第四部分开始部分)实现了它们之间的相互通讯。为了完成我们最初的用例,我们需要两个微服务——分别是“missions”和“rewards”。我将开始这个话题并按照与我们之前构建employee微服务相同的方式来构建这两个微服务,可以参考Git版本库第四部分的第一步来获得这两个微服务容器。现在如果我们执行docker ps,我们将看到如下的信息,其中的一些列为了简洁被移掉了:
CONTAINER ID IMAGE                      PORTS                         NAMES
86bd9bc19917 microservicedemo/employee 0.0.0.0:32779->8080/tcp employee
1c694e248c0a microservicedemo/reward 0.0.0.0:32775->8080/tcp reward
c3b5c56ff3f9 microservicedemo/mission 0.0.0.0:32774->8080/tcp mission
48647d735188 mongo 0.0.0.0:32771->27017/tcp mongodb


更新镜像
这都是很简单的,但是没有太大的作用,因为此时没有一个微服务可以在简单的数据CRUD功能之外带来任何直接的价值。让我们开始对一些代码的更改进行分层,以提供更多的价值和功能。我们会对某个微服务做出一些改变,然后处理如何更新镜像,了解对于容器的版本控制。由于员工通过完成任务来获得积分,我们需要追踪他们的任务完成情况、积分总计(获得和活跃的)以及奖励兑换。我们将添加一些额外的类到Employee模型中——这些不是顶层的业务对象,所以它们不会有它们自己的微服务,但是将会在Employee对象中提供上下文内容。一旦这些变化做出了(参见Git版本库第四部分第二步),将会有一些结构性的改变,需要在整个软件栈中同步,更新镜像的步骤如下:
● 重新编译源代码
gradle build

● 重新构建镜像
docker build -t microservicedemo/employee .

最后将会看到一些如下的消息:
Removing intermediate container 5ca297c19885 Successfully build 088558247

● 现在我们需要删除旧容器,替换为新的:
docker stop employee
docker rm employee
docker run -P -d --name employee --link mongodb microservicedemo/employee


需要注意的重要事项就是在运行容器内的代码是不会更新的,容器和微服务的另外一个核心原则是容器内部的代码和配置是不可变的。换句话说,你不用更新容器,只需要替换它。这对于一些容器的使用案例会造成一些问题,比如使用容器来操作数据库或者其它的持久化资源。

使用Docker Compose来编排容器
像我一样,如果你在本系列文章之间有其他的工作要做,如何确保所有的各种命令行参数都可以来连接这些容器,可能有点令人沮丧。编排这一队容器就是Docker Compose(以前被成为Fig)的目的。在Yaml配置文件中定义你的一组容器,并且管理这些容器的运行时配置。在很多方面,可以把Docker Compose想象成为一个编排者,确保“正运行”的容器有着正确的选项和配置。我们将通过命令行参数为我们的应用创建一个这样的编排者来做所有管理想做的事情。
docker-compose.yml:
employee:
build: employee
ports:
- "8080"
links:
- mongodb
reward:
build: reward
ports:
- "8080"
links:
- mongodb
mission:
build: mission
ports:
- "8080"
links:
- mongodb
mongodb:
image: mongo


接着在命令行上敲入:
docker-compose up -d


然后整个一队容器都将会启动,非常方便!许多Docker命令在Docker-Compose上都有类似的命令,如果我们运行"`docker-compose ps`",我们会看到:

Name Command State Ports
-------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp


容器的动态伸缩和负载均衡
不过上述这些还不是Docker Compose可以做的所有工作,如果你运行“`docker-compose scale [compose container name]=3`”,,将会创建多个容器实例。比如运行“`docker-compose scale employee=3`”,接着运行“`docker-compose ps`”,将会看到:

Name Command State Ports
---------------------------------------------------------------------------------
git_employee_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32789->8080/tcp
git_employee_2 java -Dspring.data.mongodb ... Up 0.0.0.0:32791->8080/tcp
git_employee_3 java -Dspring.data.mongodb ... Up 0.0.0.0:32790->8080/tcp
git_mission_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32785->8080/tcp
git_mongodb_1 /entrypoint.sh mongod Up 27017/tcp
git_reward_1 java -Dspring.data.mongodb ... Up 0.0.0.0:32784->8080/tcp

我们的employee容器现在有了三个实例!Docker Compse记得你设置的数量,所以下次运行的时候,将会启动三个employee容器实例,就我个人而言,我认为这个应该在docker-compose.yml文件中设置,但是其实不是的。

希望你开始看到一个问题如何发展的。我们应该如何构建一个针对终端用户的事实上使用微服务的应用呢?因为容器的端口变了,并且在一个Docker集群服务器环境中(比如Docker Swarm),宿主机的IP地址也会改变。有一些先进的解决方案(Kubernetes和AWS的ECS),但是现在我们将寻找一个相对简单的选项,将采用一个非常容易的方法来对容器实例做负载均衡。Tutum是一家构造多重云容器组织能力的公司,已经给Docker社区提交了一个HAProxy的扩展插件,这个扩展插件会基于连接的容器自动配置其自身。让我们为多个employee容器实例添加一个负载均衡器,我们将其添加到docker-compose.yml文件中:

ha_employee:
image: tutum/haproxy
links:
- employee
ports:
- "8080:80"


接着我们运行“`docker-compose up -d`”,将会下载缺失的镜像并且启动容器。现在我们可以再次在特定的端口(8080)运行测试,这次将会对于所有运行的employee容器进行负载均衡。接着,我们可以在192.168.99.100:8080上敲击employee服务集群,默认情况下将会轮循这三个实例。易如反掌!HAProxy的Docker容器还有许多额外的特性和功能点,我建议可以从https://github.com/tutumcloud/haproxy获得更多的信息。

HAProxy对于一个特定容器的多个实例的负载均衡处理地非常巧妙,是单容器环境的理想选择。然而,我们没有这样的环境,那咋办?我们可以启动多个HAProxy实例来处理容器集群,在宿主机的不同端口上转发每一个HAProxy容器端口,所以我们的employee服务放在了8080端口,mission服务放在8081端口,reward服务放在8082端口(参考Git版本库第四部分第三步)。如果我们来到生产环境,我们可以利用Nginx来创建反向代理,将所有的服务请求转发到一个单独的IP地址和端口上(通过URL路径/employee/和/reward/路由到相应的容器)。或者我们可以使用一个更加健壮的服务发现路由,比如这儿的利用etcd和一些让人印象深刻的Docker元数据脚本和模板引擎,来自于Jason Wilder的Docker-gen系统(https://hub.docker.com/r/jwilder/docker-gen/),以及大量的额外的自我管理的服务发现的解决方案。我们目前将会保留这个简单的HAProxy解决方案,因为它给与了我们一个对于如何管理容器集群的扎实的理解。

这是一个结束本系列的好地方。我可以说还有有许多额外的领域还没有涉及到,包括:
● 构建一个前端的容器,或者一个移动APP容器
● 包含后端的数据批处理过程
● 动态分级的容器集群来处理消息队列的条目
● 将服务从Java/Spring Boot迁移到Scala/Akka/Play
● 建立持续集成
● 构造自己的镜像Repository或者使用容器Repository服务(Google或者Docker Hub)
● 评估容器管理系统比如AWS的ECS或者Kubernetes


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART IV(翻译:胡震)

Spring Boot与Docker(三):构建你的第一个微服务和相关容器以及容器的连接

国会山上的猫TuxHu 发表了文章 • 2 个评论 • 5167 次浏览 • 2015-12-14 00:05 • 来自相关话题

【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包 ...查看全部
【编者的话】本篇是《使用Spring Boot和Docker构建微服务架构》系列的第三篇,本篇我们将会准备开始构建一个员工对象微服务。原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务、B2B集成、空间分析、SOA架构、大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先后就职于Oracle、ChoicePoint和Booz Allen Hamilton。Dan毕业于乔治·华盛顿大学,他也是一个父亲、业余木工爱好者,还参加过包括国际障碍大赛这样的障碍赛跑。

本篇构建微服务的步骤如下:

* 建立一个新的Spring Boot工程
* 定义我们的员工对象
* 持久连接
* 公开Web服务
* 定义一个Docker容器来运行我们的微服务,包括连接到我们在第二篇中创建的Mongo容器
* 在稍早设置的Docker Machine中运行我们的容器

建立我们的Spring Boot工程
我们将在我们的工程根目录下创建一个文件夹(生产环境中是在一个单独的版本库中)来承载我们的服务,在这个文件夹中,我们将创建build.grade文件。Spring Boot的优势就是在于对于依赖定义非常纯粹,如魔法般地带来了大量的互操作性。我们的build.grade文件看起来如下:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.0.RELEASE'
}
}
apply plugin: 'spring-boot'

repositories { jcenter()
}
dependencies {
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.boot:spring-boot-starter-web"
}

现在,正如我们提到的,我们将使用IDE,具体地说就是Spring Tool Suite(STS),所以我们将增加对于Gradle的支持。首先,打开STS,接着在打开的Dashboard页面上点击“IDE Extensions”的链接:
403x220xmicroservice-part-3-pic-1.jpg.pagespeed_.ic_.XIVhOVlbxA_.jpg

在屏幕上选择“Grade Support”,点击“Install”,按照提示完成安装,其中包括重启STS:
468x127xmicroservice-part-3-pic-2.jpg.pagespeed_.ic_.K4Ui_72tTm_.jpg

重启之后,选择“Import project…”,接着选择Gradle project(现在可用了),指向你的目录,然后点击“Build Model”——这将会用“Employee”来产生项目列表——选择这个列表并且点击“Finish”,这将会导入一个简单的build.grade到我们启动的工程。一旦工程被导入之后,第一个将是Spring Boot配置类,在这个场景中,我们将按照他们的服务来命名,所以在这种情况下配置类将会命名为EmployeeBoot。Spring大量使用了注解和反射,所以最小的配置也是需要的(忽略导入):
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class EmployeeBoot {

public static void main(String[] args) {
SpringApplication.run(EmployeeBoot.class);
}
}

我们的Employee类
接下来,我们将使得我们的POJO类来持有员工信息,我们目前保持最少的字段,有必要的话我们将增加:
Employee.Class

@Document(collection=”employees”)
public class Employee {

@Id
private String id;

private String email;
private String fullName;
private String managerEmail;

// getters and setters omitted for brevity
}

注意@Document这个注解——这会将这个对象连结为一个Mongo文档,并为该Employee“文档”在哪里存储指定了集合名字。
接下来我们将定义一个Spring持久类来读取这些Employee:
EmployeeRepository.class:
public interface EmployeeRepository extends
MongoRepository {
}

Spring框架的一个优美之处就是在于所有你需要写的——比如扩展自MongoRepository的接口甚至不需要手动编写实现代码。两个通用的参数中第一个表示需要持久化的对象类型(Employee),第二个表示唯一的标识符类型(String类型)。Spring框架将会自动提供所声明的功能95%的实现。接下来的问题就是:Spring如何知道数据库的位置?Spring默认情况下将会寻找localhost:27017,这显然是不会工作的,所以我们需要直接设置位置新型。我们可以实现自己的Mongo模板Bean,但是幸运的是Spring允许我们通过Java属性表传递连接信息。我们可以定义一个属性文件或者在命令行上传递进来。我们选择了后者因为当稍后构建我们的容器时命令行方式非常方便。最后我们需要创建的是一个或者两个Rest端点并确保它们可以工作。我们将构建一个快速的Spring Controller,然后我们就可以测试了。
EmployeeController.java

@RestController
@RequestMapping("/employee")
public class EmployeeController {

@Autowired
EmployeeRepository employeeRepository;

@RequestMapping(method = RequestMethod.POST)
public Employee create(@RequestBody Employee employee){

Employee result = employeeRepository.save(employee);
return result;
}

@RequestMapping(method = RequestMethod.GET, value="/{employeeId}")
public Employee get(@PathVariable String employeeId){
return employeeRepository.findOne(employeeId);
}
}

最开始的@RestController和@RequestMapping这两个类级别的注解告诉Spring框架需要公开这是一个接收JSON的Rest服务,并且公开了URI路径是/employee。@Autowired注解告诉Spring框架采用上面我们定义的Repository接口的自动生成实现代码并将其注入到这个Controller中。现在到了特定的操作——方法级别的@RequestMapping注解表明这个方法将会基于HTTP动词来使用(在本例中是POST和GET),另外对于GET操作,我们指明了一个URL路径{employee},比如使用/employee/abcd1234来寻找一个员工并且返回该值。
现在我们有了足够多的准备工作可以来测试了,第一点,我们需要编译和运行我们的Spring Boot应用,在Eclipse中有很多方法可以做到这一点,但是我们将从命令行开始,并以我们的方式工作。在你的Employee目录中,敲入:`gradle build`,这个命令将会编译Spring Boot应用到build/lib/Employee.jar,这个jar包包含了运行这个应用所需要的所有东西,包括一个嵌入式的Servlet容器(默认情况下是Tomcat)。
在我们运行和测试这个应用之前,我们需要稍作回顾——我们的Mongo服务又在哪里?观察“docker ps”命令的输出,我们记得虚拟机的32777端口映射到了Mongo容器的27017端口,虚拟机的IP地址是192.126.99.100。如前所述,我们可以通过传递一个环境变量属性的方式来把连接属性传递给Spring,所以运行这个应用的命令行如下:
java -Dspring.data.mongodb.uri=mongodb://192.168.99.100:32777/micros -jar build/libs/Employee.jar

一旦应用启动之后(应该在1~4秒),你可以使用选项中的Rest工具来打开Web服务。

456x171xmicroservice-part-3-pic-3.jpg.pagespeed_.ic_.wWMISBC68u_.jpg

不要忘了在HTTP头部包含值为“application/json”的“Content-Type”,你应该收到如下的响应(id的值取决于你自己的情况):
{
"id": "55fb2f1930e07c6c844b02ff",
"email": "dan.greene@3pillarglobal.com",
"fullName": "Daniel Greene",
"managerEmail": null
}

你可以通过如下的调用测试我们的GET方法:
http://localhost:8080/employee/55fb2f1930e07c6c844b02ff

你应该得到相同的文档,万岁!我们的服务起作用了!

将Boot应用导入容器
现在我们需要构建我们的第一个容器来运行我们的Employee微服务。我们的途径是定义一个Dockerfile,这个文件将会定义如何生成一个镜像并放入我们的精益求精、短小精悍的微服务中。让我们阅读这个文件并逐步解析(参考第三篇第三步请跳到这儿):
FROM java:8
VOLUME /tmp
ADD build/libs/Employee.jar app.jar
EXPOSE 8080
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Dspring.data.mongodb.uri=mongodb://mongodb/micros", "-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]


* 我们从一个包含了安装了Java 8的标准镜像(镜像名字为“Java”,标签为“8”)开始构建过程
* 我们接着定义了一个名为/tmp的卷
* 接着将本地文件系统的一个文件添加进来,并且重命名为“app.jar”,重命名不是必须的,只是一个可用的可选项
* 我们声明想要公开容器的8080端口
* 在容器内运行一个touch命令,这样可以确保app.jar文件的修改日期
* ENTRYPOINT命令定义了容器启动时需要运行的内容——我们运行Java,设置我们的Spring Mongo属性,还有快速的附加属性来加速Tomcat启动时间,然后指向我们的jar包。

现在我们通过运行如下命令构建镜像:
docker build -t microservicedemo/employee . 

我们可以敲入`docker images`看到结果
REPOSITORY                  TAG             IMAGE ID            CREATED             VIRTUAL SIZE
microservicedemo/employee latest 364ffd8162b9 15 minutes ago 846.6 MB


接下来的问题就是“我们的服务容器如何与Mongo容器交互?”,为此,我们引入了容器的link机制。当你运行一个新的容器时,你可以传入一个可选的-link参数指定一个运行中的容器的名字,这样新的容器就可以与这个容器通讯了,所以我们的命令是:

docker run -P -d --name employee --link mongodb microservicedemo/employee


我们启动了一个新的容器,公开了端口(-P),以后台方式运行(-d),命名为employee(—name),接着将这个新的容器连接到了一个名为“mongodb”的容器(-link),连接过程如下:

* 在employee容器的host文件添加一个条目指向MongoDB容器的运行位置
* 在employee容器内添加一些环境变量来协助其他必要的编程访问,可以运行如下命令查看:
docker exec employee bash -c 'env | grep MONGODB'

* 允许容器通过公开的端口来直接通讯,这样就不需要担心主机的部分映射了。如果你还记得上述的内容,我们设置了Spring Mongo到MongoDB的URL作为主机名(mongodb://mongodb/micros),所以有了host文件条目和运行在默认端口的Mongo,Boot应用容器可以连上数据库了

在容器运行时我们可以来执行相同的Web服务,只是这次是作为容器来运行的(对于我来说,容器的8080端口会被映射到虚拟机的32772端口):
467x483xmicroservice-part-3-pic-4.jpg.pagespeed_.ic_.2e11ONwBrl_.jpg

本篇我们已经取得了很大的进展,我们有了两个容器可以工作并且可以互相通讯,接下来的第四篇我们将添加一些额外的服务/容器,可以观察到构建更新的进度并与CI工具集成工作。


原文链接:BUILDING A MICROSERVICE ARCHITECTURE WITH SPRING BOOT AND DOCKER, PART III(翻译:胡震)
Spring Boot这个框架在经历了不断的演变之后,如今已经能够用于开发Java微服务了。Boot是基于Spring框架进行开发的,也继承了Spring的成熟性。它通过一些内置的固件封装了底层框架的复杂性,以帮助使用者进行微服务的开发。Spring Boot的一大优点是提高开发者的生产力,因为它已经提供了许多通用的功能,例如RESTful HTTP以及嵌入式的web应用程序运行时,因此很容易进行装配及使用。在许多方面上,它也是一种“微框架”,允许开发者选择在整个框架中他们所需的那部分,而无需使用庞大的、或是不必要的运行时依赖。