项目作者: SamMACode

项目描述 :
spring cloud micro-service distributed architecture and deployed apps to kubernetes container-orchestration system
高级语言: Java
项目地址: git://github.com/SamMACode/springcloud.git
创建时间: 2018-09-22T13:31:42Z
项目社区:https://github.com/SamMACode/springcloud

开源协议:Apache License 2.0

下载


Spring-Logos-CLOUD-HOR-1200x350

微服务系统设计

“微服务就是一些协同工作的小而自治的服务,微服务很小,只专注做好一件事。一个微服务就是一个独立的实体,它可以独立的部署在PAAS(Platform As A Service)上,也可以作为一个操作系统进程存在。”

微服务具有很多不同的好处,其中很多好处也适用于任何一个分布式系统:系统技术的异构性,在一个由多个服务相互协作的系统中,可以在不同的服务中使用最适合该服务的技术。提高系统的弹性,弹性工程学的一个关键概念就是舱壁,如果系统中一个组件不可用了,但是并没有导致级联故障,那么系统的其它部分还可以正常运行。系统易于扩展,使用较小的多个服务,则可以只对需要扩展的服务进行扩展,这样就可以把那些不需要扩展的服务运行在更小的、性能稍差的硬件上。

服务整体设计图

服务之间的调用关系如下所示,服务结构图详细描述了从Reactive Gateway到具体服务及后端数据源Datasource之间的调用流程:eureka-server作为服务注册中心用于服务发现、Reactive-gateway作为微服务网关其对外提供统一访问入口,在网关层统一做request鉴权、请求分发、限流策略等。config-server为应用配置中心提供统一的配置管理功能,其从github指定仓库获取配置文件。oauth-service基于spring cloud security实现,其主要提供鉴权校验功能。

image-20200809152824756

服务列表及所依赖spring cloudspring boot版本信息如下:

ServiceName Spring Boot Spring Cloud ORM Messaing Service Type
eureka-server 2.1.12.RELEASE Greenwich.SR5 R2DBC N/A netflix eureka
api-gateway 2.1.12.RELEASE Greenwich.SR5 R2DBC N/A spring cloud gateway
config-server 2.1.12.RELEASE Greenwich.SR5 N/A N/A config center
oauth-service 2.1.12.RELEASE Greenwich.SR5 R2DBC N/A spring cloud oauth
order-service 2.1.12.RELEASE Greenwich.SR5 R2DBC apache kafka domain
product-service 2.1.12.RELEASE Greenwich.SR5 R2DBC apache kafka domain
user-service 2.1.12.RELEASE Greenwich.SR5 R2DBC N/A domain

Api Gateway对外提供的接口列表

通过kubernetes在本地部署应用

服务运行依赖与MySQLKafkaRedisRabbitMQ组件,已在docker中启动相应实例,通过docker ps命令查看:

  1. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  2. d8c58b66f42c wurstmeister/kafka "start-kafka.sh" 4 weeks ago Up 24 hours 0.0.0.0:9092->9092/tcp kafka
  3. e1cd62ea621a wurstmeister/zookeeper "/bin/sh -c '/usr/sb…" 4 weeks ago Up 24 hours 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp zookeeper
  4. 742e2bdf8ceb rabbitmq "docker-entrypoint.s…" 4 weeks ago Up 24 hours 4369/tcp, 0.0.0.0:5672->5672/tcp, 5671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
  5. e7e23c2459a5 redis "docker-entrypoint.s…" 4 weeks ago Up 24 hours 0.0.0.0:6379->6379/tcp redis-server
  6. e2602c9d96b4 mysql:5.6 "docker-entrypoint.s…" 7 weeks ago Up 24 hours 0.0.0.0:3306->3306/tcp mysql

由于kubectl使用阿里云镜像中心作为镜像仓库,因此需要将要部署的镜像上传到阿里云镜像中心(公开镜像),kubectl根据deployment中镜像名称去阿里云仓库下载并在kubernetes部署:

  1. % sudo docker images | grep aliyun
  2. registry.cn-shanghai.aliyuncs.com/spotify-music/config-server latest a86a7d0bdb0c 21 hours ago 694MB
  3. registry.cn-shanghai.aliyuncs.com/spotify-music/user-service latest ffb510167232 21 hours ago 711MB
  4. registry.cn-shanghai.aliyuncs.com/spotify-music/product-service latest 1584a7a9e04f 21 hours ago 715MB
  5. registry.cn-shanghai.aliyuncs.com/spotify-music/api-gateway latest 5c4fd84f7307 21 hours ago 695MB
  6. registry.cn-shanghai.aliyuncs.com/spotify-music/order-service latest a22a0a1a54c3 21 hours ago 726MB
  7. registry.cn-hangzhou.aliyuncs.com/spotify-music/eureka-server latest 28a756e96fd5 22 hours ago 689MB
  8. registry.cn-shanghai.aliyuncs.com/spotify-music/eureka-server latest 28a756e96fd5 22 hours ago 689MB

编写应用部署的yml文件(以eureka-server为例),如下eureka-server-k8s-deploy.ymleureka配置文件:应用部署的namespacesvc,应用类型为Deployment类型控制应用副本以及应用升级,同时liveness存活探针指向服务的8762端口(检测应用是否可用,否则k8s会启动新的实例)。

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: eureka-server
  5. namespace: svc
  6. spec:
  7. selector:
  8. matchLabels:
  9. app: eureka-server
  10. replicas: 2
  11. template:
  12. metadata:
  13. labels:
  14. app: eureka-server
  15. spec:
  16. containers:
  17. - name: eureka-server
  18. livenessProbe:
  19. httpGet:
  20. path: /actuator/health
  21. port: 8762
  22. initialDelaySeconds: 10
  23. timeoutSeconds: 15
  24. periodSeconds: 60
  25. failureThreshold: 6
  26. image: registry.cn-shanghai.aliyuncs.com/spotify-music/eureka-server:latest
  27. ports:
  28. - containerPort: 8762

通过deploymentspec.replicas启动了多个应用实例,由于Pod可能随时启动或关闭、同时Pod也存在水平扩展多个实例,对其中某个固定pod发起请求并不合理。此时需要一个单一不变的接入点请求应用(引入Service资源),其使用round轮询或者随机方式转发请求,可确保服务一直处理可用状态。 eureka-server-service.yml服务配置如下:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: eureka-server
  5. namespace: svc
  6. spec:
  7. ports:
  8. - port: 8762
  9. targetPort: 8762
  10. selector:
  11. app: eureka-server

spring cloud服务发现进行整合的思考:各个微服务最终都会注册到eureka注册中心上,控制面板上显示微服务实例host:port地址。当存在使用feign调用其它服务时,此时在当前实例pod上会将要调用服务的host转换为对应ip地址(pod地址),然后进行服务请求。在kube-system命名空间下存在一个pod其为kube-dns,集群中其它的pod都被配置成使用其作为dnsfeign服务调用也是一样,kube-dns会按service.namespace格式从service列表中进行匹配找相应pod实例(不存在namespace默认从default命名空间找相应service),然后将具体pod地址进行返回。

微服务host是通过application.yml文件中spring.instance.hostname参数进行配置的,配置内容应写为k8sservice.namespace格式,否则就会出现host无法解析的问题。

  1. spring:
  2. application:
  3. name: eureka-server
  4. instance:
  5. hostname: eureka-server.svc
  6. metadata-map:
  7. instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}

minikube上对eureka-server进行部署:kubectl create -f eureka-server-k8s-deploy.yml

创建service对外统一提供服务:kubectl create -f eureka-server-service.yml,当对配置文件存在修改时,使用kubectl replace -f eureka-server-service.yml对服务进行更新。

  1. % kubectl get pods -n svc
  2. NAME READY STATUS RESTARTS AGE
  3. api-gateway-6d7d9cc4d7-czjvd 1/1 Running 11 10h
  4. config-server-8d7866488-6vmft 1/1 Running 9 11h
  5. eureka-server-7fcfcb6c59-l2lsr 1/1 Running 5 11h
  6. eureka-server-6sdf9cb081-23ndp 1/1 Running 4 11h

要打算从宿主机的port访问eureka dashboard可使用kubectl port-forward eureka-server-6sdf9cb081-23ndp 8762:8762 --namespace svc命令,此时即可通过宿主机的8762端口进行服务访问。

使用Netflix Hystrix的客户端弹性模式

客户端弹性软件模式的重点是:在远程服务发生错误或表现不佳时保护远程资源(另一个微服务调用或者数据库资源)的客户端免于崩溃。这些模式的目标是让客户端“快速失败”,而不消耗诸如数据库连接和线程池之类的宝贵资源。共有4种客户端弹性模式,分别是:客户端负载均衡(client load balance)模式、断路器(circuit breaker)模式、后备(fallback)模式、舱壁(bulkhead)模式。

客户端负载均衡主要是通过netflix eureka查找服务的所有实例,然后缓存服务实例的物理位置。每当有服务消费者需要调用服务实例时,客户端负载均衡器将从它维护的服务位置池返回一个位置。默认使用Netflix Ribbon进行客户端服务调用,当客户端均衡器检测到问题,它可以从可用服务位置池中移除该服务实例,并防止将来的服务调用访问该服务实例。

spring cloud使用hystrix实现断路器模式,通过在方法上标注@HystrixCommand注解来将Java类方法标记为由Hystrix断路器进行管理。其将动态生成一个代理,该代理将包装该方法,并通过专门用于处理远程调用的线程池来管理对该方法的所有调用。

  1. @HystrixCommand(commandProperties = {
  2. @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 设置熔断是否开启
  3. @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 表示在滚动期断路器的最小请求数
  4. @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 设置熔断器时间窗的时间
  5. @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"), // 表示打开熔断器的百分比(当调用失败达到60%以上时候)
  6. @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
  7. }, fallbackMethod = "fallback")
  8. @GetMapping(value = RequestConstInfo.HYSTRIX_GET_PRODUCT_LIST)
  9. public String getProductInfoList(@RequestParam("number") Integer number) {
  10. if(number % 2 == 0) {
  11. return "success";
  12. }
  13. RestTemplate restTemplate = new RestTemplate();
  14. return restTemplate.postForObject("http://localhost:8083/product/listForOrder",
  15. Arrays.asList("157875196366160022"),
  16. String.class);
  17. }
  18. private String fallback(Integer number) {
  19. return "太拥挤了,请稍后重试~~";
  20. }

hystix默认的断路器时间为1000ms,熔断超时时间可通过@HystixCommand注解的execution.isolation.thread.timeoutInMilliseconds进行配置,当服务调用超过配置的时间时,会自动进行熔断。在后备处理模式中,通过给@HystrixCommandfallbackMethod属性指定默认方法用于在发生熔断时进行调用。

舱壁模式是建立在造船的概念基础上的,采用舱壁设计,一艘船被划分为完全隔离和防水的隔间,这称为舱壁。即使船的船体被击穿,由于船被划分为水密舱(舱壁),舱壁会将水限制被击穿的船的区域内,防止整艘船灌满水并沉没。hystrix使用线程池进行资源的隔离,每个远程资源都是隔离的,并分配给线程池。如果一个服务响应缓慢,那么这种服务调用的线程池就会饱和并停止处理请求,而对其它服务的调用则不会变得饱和,因为它们被分配给了其他线程池。

  1. @HystrixCommand(threadPoolKey = "product-service-threadpool",
  2. threadPoolProperties = {
  3. @HystrixProperty(name = "coreSize", value = "30"),
  4. @HystrixProperty(name = "maxQueueSize", value = "10")
  5. })
  6. @GetMapping(value = RequestConstInfo.GET_PRODUCT_LIST)
  7. public String getProductList() {
  8. List<ProductInfoOutput> response = productClient.listForOrder(Arrays.asList("157875227953464068"));
  9. log.info("response => {}", response);
  10. return "ok";
  11. }

@HystrixCommand注解中引入了一个新属性threadPoolKey,其表明我们想要建立一个新的线程池。要定制线程池配置,应使用其threadPoolProperties属性,此属性使用HystrixProperty对象的数组控制线程池的行为,使用coreSize属性可以设置线程池的大小、用maxQueueSize设置请求队列的大小,一旦请求数超过队列大小,对线程池的任何其他请求都将失败,直到队列中有空间。

使用Spring Cloud Sleuth和Zipkin进行分布式跟踪

微服务架构是一种强大的设计范型,可以将复杂的单体软件系统分解为更小、更易于管理的部分。这些可管理的部分可以独立构建和部署,其提供的灵活性提升了服务管理上复杂度。服务的分布式特性意味着必须在多个服务、物理机器和不同的数据存储之间跟踪一个或多个事务。使用关联ID将跨多个服务的事务链接在一起、将来自多个服务的日志聚合为一个可搜索的源、可视化跨多个服务的用户事务流并理解事务每个部分的性能特征。

springcloud-zipkin

可使用sleuthzipkin进行分布式链路跟踪,zipkin是一种开源数据可视化工具,可以显示跨多个服务的事务流。zipkin允许开发人员将事务分解到它的组件块中,并可视化地识别可能存在性能热点的位置。