关闭
Hit
enter
to search or
ESC
to close
May I Suggest ?
#leanote #leanote blog #code #hello world
Okeeper's Blog
Home
Archives
Tags
DevOps
软件笔记
Spring
学习
JVM系列
关于我
Spring Cloud微服务实践
无
417
0
0
zhangyue
# 什么是Spring Cloud 看完《微服务设计》后,算是补上了自己在服务化这块的理论知识,在业界,一般有两种微服务的实践方法:基于dubbo的微服务架构、基于Spring Cloud的微服务架构。从概念上来讲,Dubbo和Spring Cloud并不能放在一起对比,因为Dubbo仅仅是一个RPC框架,实现Java程序的远程调用,实施服务化的中间件则需要自己开发;而Spring Cloud则是实施微服务的一系列套件,包括:服务注册与发现、断路器、服务状态监控、配置管理、智能路由、一次性令牌、全局锁、分布式会话管理、集群状态管理等。 我们基于Dubbo实施服务化,刚开始是基于ZooKeeper进行服务注册与发现,现在已经转成使用Etcd。我这次学习Spring Cloud,则是想成体系得学习下微服务架构的实现,也许能够对基于Dubbo实施微服务架构有所借鉴。 # Spring Cloud能做什么 Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作提供了一种简单的开发方式。 Spring Cloud包含了多个子项目(针对分布式系统中涉及的多个不同开源产品),比如:Spring Cloud Config、Spring Cloud Netflix、Spring Cloud0 CloudFoundry、Spring Cloud AWS、Spring Cloud Security、Spring Cloud Commons、Spring Cloud Zookeeper、Spring Cloud CLI等项目。 Spring Cloud: - Spring Cloud Config:依靠git仓库实现的中心化配置管理。配置资源可以映射到Spring的不同开发环境中,但是也可以使用在非Spring应用中。 - Spring Cloud Netflix:不同的Netflix OSS组件的集合:Eureka、Hystrix、Zuul、Archaius等。 - Spring Cloud Bus:事件总线,利用分布式消息将多个服务连接起来。非常适合在集群中传播状态的改变事件(例如:配置变更事件) - Spring Cloud Consul:服务发现和配置管理,由Hashicorp团队开发。 我决定先从Spring Cloud Netflix看起,它提供了如下的功能特性: - **服务发现**:Spring Cloud Netflix Eureka(基于AP,可用性,分区容错性) 、Spring Cloud Consul(CA,一致性,可用性) - **断路器**:利用注解,可以创建一个简单的Hystrix客户端;通过Java配置文件可以创建内嵌的Hystrix控制面板; - **声明式REST客户端**:使用Feign可以创建声明式、模板化的HTTP客户端; - **客户端负载均衡器**:Ribbon,主要提供客户侧的软件负载均衡算法。 - **路由器和过滤器(网关)**:Zuul可以在微服务架构中提供路由功能、身份验证、服务迁移、金丝雀发布等功能。 > 关于CAP理论解释如下: C (Consistency) 一致性,即当写入数据发生变化时,读能立即获取到最新变化 A(Availability) 可用性,即当有部分机器故障时,任然能够对外提供服务的能力 P(Paring tolerant) 分区容错性,即当发生网络分区时也能对外提供服务的能力 实践证明这三种特性最多只能同时满足两个,即我们在设计分布式系统时候必须要对CAP做分析取舍,如果有谁试图设计出完美支持者三种特性的系统是非常愚蠢的。 # 服务注册发现 ## Spring Cloud Netflix Eureka 首先,我们来尝试使用Spring Cloud Eureka来实现服务治理。 Spring Cloud Eureka是Spring Cloud Netflix项目下的服务治理模块。而Spring Cloud Netflix项目是Spring Cloud的子项目之一,主要内容是对Netflix公司一系列开源产品的包装,它为Spring Boot应用提供了自配置的Netflix OSS整合。通过一些简单的注解,开发者就可以快速的在应用中配置一下常用模块并构建庞大的分布式系统。它主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。 Eureka是专门为服务注册发现开发的,它是Spring Clound Netflix组件之一,它是基于AP理论设计,在一致性方面做了一些妥协。它的设计哲学是,宁愿返回一个5分钟之前可用的服务,也不要因为暂时的网络问题(或者宕机)而找不到服务。 下面,就来具体看看如何使用Spring Cloud Eureka实现服务治理。 ### Eureka Server 1. 新建一个Spring Boot项目,添加Eureka Server引用 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> ``` 2. Spring Boot 启动类加上`@EnableEurekaServer`注解启动Eureka Server插件 3. 在Spring Boot配置文件`application.yml`中配置Eureka Server的端口和地址 ``` server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false #表示不需要将此项目的的服务注册到Eureka Server中,因为这个项目本身不提供服务,并且是Eureka Server. fetchRegistry: false #表示不需要拉取Eureka Server中的服务到本地缓存,因为这个项目不需要使用任何依赖的服务 serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ ``` > 由于Spring Boot 的` @ConfigurationProperties`支持松散绑定`Relaxed binding`,所以属性名使用标准的驼峰标识和使用中划线连接都可以映射成Java Bean的属性。如`eureka.client.registerWithEureka` 等同于 `eureka.client.register-with-eureka` 或者 `eureka.client.register_with_eureka`都可以,关于[Relaxed Binding请参阅官方说明文档](https://docs.spring.io/spring-boot/docs/1.5.3.RELEASE/reference/htmlsingle/#boot-features-external-config-relaxed-binding) ### Eureka Client 服务注册 1. 在提供服务的Spring Boot 项目中`pom.xml`中添加 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> ``` 2. 在Spring Boot 启动类中加上`@EnableEurekaClient`注解开启Eureka Client插件 3. 在Spring Boot 配置文件中配置Eureka Server的地址 ``` # 配置这个服务的serviceId spring.application.name=hello-service # 配置这个Eureka Server的地址 eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/ ``` ### Eureka Client 服务发现 1. 也需要在项目`pom.xml`中加入Eureka 的支持 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> ``` 2. 在项目启动类中添加注解`@EnableDiscoveryClient` 激活eureka中的DiscoveryClient实现 3. 配置Eureka Server的地址`eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/` 4. 在需要发现服务的地方使用`DiscoveryClient`手动发现服务 ``` @Autowired DiscoveryClient discoveryClient; @RequestMapping("/hello") public String testHello() { //查找服务 String serviceRootPath = findServiceRootPath("simple-service1"); RestTemplate restTemplate = new RestTemplate(); //GET String result = restTemplate.getForObject(serviceRootPath + "/hello?name={name}",String.class,"韩梅梅"); return "访问远程服务返回:" + result; } /** * 发现服务 * @param serviceId * @return */ public String findServiceRootPath(String serviceId) { List<ServiceInstance> list = discoveryClient.getInstances(serviceId); if (list != null && list.size() > 0 ) { URI uri = list.get(0).getUri(); if (uri !=null ) { return uri.toString(); } } return null; } ``` ## Eureka 的一些配置 Eureka Server ``` # 自我保护机制。Eureka Server在运行期间,会统计心跳成功率在15分钟之内是否低于85%, # 如果出现低于的情况(在单机调试的时候很容易满足,实际在生产环境上通常是由于网络不稳定导致), # Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。至于为什么需要有这个自我保护机制, # 官方的解释是:service不是强一致的,所以会有部分情况下没发现新服务导致请求出错, # 对于Service发现服务而言,宁可返回某服务5分钟之前在哪几个服务器上可用的信息,也不能因为暂时的网络故障而找不到可用的服务器,而不返回任何结果 eureka.server.enableSelfPreservation=false #是否开启服务保护模式,即当服务时效是立即注销,不对其保护,防止服务调用方访问到时效服务 # 为了保证服务的快速发现和注销,可以配置如下参数 eureka.instance.leaseRenewalIntervalInSeconds=1 #心跳间隔时间1s ,默认间隔30s eureka.instance.leaseExpirationDurationInSeconds=2 #连续2s未响应时将服务注销,服务时效时间,默认90s ``` Eureka Client ``` eureka.client.registerWithEureka: false #表示不需要将此项目的的服务注册到Eureka Server中,因为这个项目本身不提供服务,并且是Eureka Server.默认是true eureka.client.fetchRegistry: false #表示不需要拉取Eureka Server中的服务到本地缓存,因为这个项目不需要使用任何依赖的服务,默认是true eureka.client.serviceUrl.defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ ``` # 负载均衡及路由Ribbon 上面讲到的使用`DiscoveryClient`来注册发现服务是不是感觉实在费劲,并且返回的是一堆服务提供方的地址列表,具体要访问那一台还得自己实现路由算法,用的十分变扭,有没有一个这样的组件能够帮我们实现呢,答案是肯定的,那就是`Ribbon`. Ribbon是Netflix发布的云中间层服务开源项目,主要功能是提供客户端负载均衡算法。Ribbon客户端组件提供一系列完善的配置项,如,连接超时,重试等。简单的说,Ribbon是一个客户端负载均衡器,我们可以在配置文件中列出load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。 ## 使用 在`RestTemplate` 上加上注解`@LoadBalanced`即可, ``` @Configuration @RestController public class TestRibbonController { @Bean //定义REST客户端,RestTemplate实例 @LoadBalanced //开启负债均衡的能力,加了此注解之后默认将在RestTemplate的Interceptor中加上`LoadBalancerInterceptor`,而Spring Cloud默认就是使用`RibbonLoadBalancerClient`实现`LoadBalancerClient`接口的,所以很方便就集成了Ribbon的负载均衡功能。 RestTemplate restTemplate() { return new RestTemplate(); } @Autowired private RestTemplate restTemplate; @GetMapping(value = "/testRibbon") public String testRibbon() { return "LoadBalanced access :" + restTemplate.getForEntity("http://hello-service/hello", String.class).getBody(); } } ``` 或者使用`LoadBalancerClient`获取具体service实例url在通过RestTempldate访问 ``` @Autowired private LoadBalancerClient loadBalancerClient; @Autowired private RestTempldate restTempldate; @Bean //定义REST客户端,RestTemplate实例 RestTemplate restTemplate() { return new RestTemplate(); } @GetMapping(value = "/testLoadBalance") public String testRibbon() { String url = loadBalancerClient.choose("hello-service").getUri().toString() + "/hello"; log.info("url:" + url); return "TestLoadBalanceController >>" + restTemplate.getForEntity(url, String.class).getBody(); //return "LoadBalanced access :" + restTemplate.getForEntity(url, String.class).getBody(); } ``` # 使用Feign声明远程服务接口,实现远程接口的本地调用效果 通过以上例子我们实现了使用RestTempldate结合负载均衡Ribbon实现了远程接口访问,当时感觉还是不够爽,比较繁琐,还需要获取远程的接口地址访问,能否做到像本地方法一样直接使用呢,那就是Feign做的事。 1. 加入`pom.xml`支持 ``` <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> ``` 2. BootApplication加入注解`@EnableFeignClients` 3. 声明远程服务接口,然后就可以像本地service一样依赖注入了 ``` @RestController public class TestFeignController { @Autowired private HelloService helloService; @RequestMapping("/testFeign") public String testFeign() { return helloService.hello("Feign"); } } @FeignClient(value = "hello-service") interface HelloService { @RequestMapping(value = "/hello", method = RequestMethod.GET) String hello(@RequestParam("name") String name); } ``` # Hystrix 服务熔断 线上服务经常会遇到网络不稳定,服务机器宕机的情况,如果出现这种情况直接体现依赖服务不可用,导致整个业务流程跑不下去。 ## Hystrix能干嘛 1. 断路器机制,Hsytrix就像电路的保险丝,当出现异常时候或者异常达到一定阀值时,触发熔断,断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力 2. Fallback 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存. 3. 资源隔离 Hystrix使用不同的线程池访问不同的服务接口,从而做到接口之间的异常隔离,即使由于某些服务接口不可用导致线程池耗尽或者程序bug异常退出也不会影响别的接口访问。 ## 使用 1.如果已经引用了Feign的依赖文件,则默认已经依赖了Hystrix相关的包,直接配置中打开开关 ``` feign.hystrix.enabled=true ``` 2.在Feign声明接口地方加入Hystrix的Callback配置 ``` @FeignClient(value = "hello-service",fallback = HelloServiceHystrix.class) interface HelloService { @RequestMapping(value = "/hello", method = RequestMethod.GET) String hello(@RequestParam("name") String name); } @Component public class HelloServiceHystrix implements HelloService{ @Override public String hello(@RequestParam(value = "name") String name) { return "hello" +name+", this is default result "; } } ``` # Demo ## git:[https://github.com/okeeper/spring-cloud-demo](https://github.com/okeeper/spring-cloud-demo)
觉得不错,点个赞?
Please enable JavaScript to view the
comments powered by Disqus.
comments powered by
Disqus
文章目录