SpringCloud微服务Eurehe和Ribbon+RestTempale/Feign组件 ゝ一世哀愁。 2022-04-06 02:00 169阅读 0赞 ### Spring Cloud微服务 ### #### 什么是Spring Cloud #### Spring体系下的微服务一站式解决方案,通常和Spring Boot整合在一起使用,可非常方便的开发出高效易用的微服务架构,Spring Cloud官方给出了21种组件的开发与支持 SpringCloud是基于SpringBoot的一整套实现微服务的框架。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。最重要的是,跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便。 #### 关于微服务 #### 单体架构中,所有的代码集中在同一个项目中。虽然便于管理,但是当项目足够庞大时, 所有的业务模块都集中在一个JVM进程中,会面临很多问题: 1、项目过于臃肿,难以维护 2、资源无法隔离,某个资源出现问题,整个系统崩溃 3、拓展性差,通常只能水平拓展,缺乏灵活性 ##### 什么是微服务 ##### SOA代表一种面向服务的架构,微服务本身就是SOA的一种延伸; 微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级机制(通常用HTTP资源API);这些服务围绕业务能力构建并且可以通过全自动部署机制独立部署;这些服务共用一个最小型的集中式的管理,服务可用不同的语法开发,使用不同的数据存储技术; ##### 微服务的特点 ##### 1.根据业务模块划分服务 2.每个服务可以独立部署并且互相隔离 3.通过轻量的API调用服务(Http,Restful,RPC,MQ) 4.服务需要保证良好的高可用性 ##### 微服务和SOA架构的区别 ##### 微服务是SOA发展出来的产物,他是一种比较现代化的细粒度的SOA实现方式 讨论微服务和SOA的差别意义远不如讨论微服务和单体系统的差别 互联网近些年的发展,越来越朝**去中心化**的方向前进了,并没有权威的机构来对它进行定义,使得每一个人都可以根据自己本身出发进行不同的调整 ##### 微服务的缺点 ##### 1.项目颗粒度太细,增加了项目管理的难度 2.远程调用带来的性能消耗 3.跨服务的测试更为复杂 4.运维的成本更高 5.需要强大的整体规划与设计能力 #### Spring Cloud的子项目 #### 官网 21种 #### 基本工程结构 #### 1.SpringBoot-pom工程 2.实现多继承 <!--解决继承多个父依赖--> <!--依赖管理--> <dependencyManagement> <dependencies> <dependency> <!--Spring Cloud工程的坐标--> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR1</version> <type>pom</type> <!--import只能用于dependencyManagement并且type必须为Pom--> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> #### 服务的发现与注册-Eureka #### ##### 什么是Eureka ##### Eureka是Spring Cloud Netflix微服务套件中的一部分,可以与Springboot构建的微服务很容易的整合起来。 Eureka包含了服务器端和客户端组件。服务器端,也被称作是服务注册中心,用于提供服务的注册与发现。Eureka支持高可用的配置,当集群中有分片出现故障时,Eureka就会转入自动保护模式,它允许分片故障期间继续提供服务的发现和注册,当故障分片恢复正常时,集群中其他分片会把他们的状态再次同步回来。 客户端组件包含服务消费者与服务生产者。在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。 #### Dubbo的工作模型和Eureka的工作模型 #### 1.zookeeper是进程,Eureka是工程 服务的提供者: zk.jar/Eureka客户端(微服务) 服务的消费者: zk.jar:发现服务获得远程调用地址/Eureka客户端(微服务) 2.在Dubbo有明确的角色,在Eureka中并没有明确,每一个微服务都需要注册,可以互相调用 3.Dubbt的协议:Dubbo,Eureka协议:Http Restful 4.ZooKeeper服务注册和发现是使用ZooKeeper协议,Euraka是使用TCP/IP协议 #### Eureka服务端 #### 配置启动类 /** * 使用一个组件 * 1.添加起步依赖 * 2.配置@EnableXXX * 3.配置文件application.yml */ @SpringBootApplication @EnableEurekaServer public class SpringcloudEurekaServerApplication { 配置application.yml server: port: 8080 eureka: instance: # 配置实例主机名称 hostname: localhost # 配置Eureka客户端 - 为集群做准备 client: service-url: # 配置map key: value形式 defaultZone是固定的 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka # 额外的配置 # Eureka认为自己也是一个微服务所以也会注册自己到服务端 # false禁止自我注册 register-with-eureka: true # false禁止注册中心调取服务 fetch-registry: false spring: application: name: eureka-server ##### 搭建Eureka注册中心集群 ##### serverName相同,hostname不同然后defaultZone配置其他两个服务端的url即可 server: port: 8080 eureka: instance: hostname: www.aaa.com client: service-url: defaultZone: http://www.bbb.com:8082/eureka,http://www.ccc.com:8083/eureka server: port: 8082 eureka: instance: hostname: www.bbb.com client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.ccc.com:8083/eureka register-with-eureka: true fetch-registry: false spring: application: name: eureka-server server: port: 8083 eureka: instance: hostname: www.ccc.com client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.bbb.com:8082/eureka register-with-eureka: true fetch-registry: false spring: application: name: eureka-server #### Eureka提供者 #### server: port: 8081 eureka: client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.bbb.com:8082/eureka,http://www.ccc.com:8083/eureka instance: # Eureka修改注册服务的标识名称与ip显示 instance-id: provider1 prefer-ip-address: true spring: application: name: provider1 #### 搭建提供者集群 #### spring-application-name相同instance-id不同即可 默认轮询 server: port: 8086 eureka: client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.bbb.com:8082/eureka,http://www.ccc.com:8083/eureka instance: instance-id: provider2 prefer-ip-address: true spring: application: name: provider1 #### Eureka自我保护机制 #### ##### 什么是自我保护机制 ##### 当Eureka服务端在一定时间内接收不到客户端(为服务)发送的心跳,注册中心本应该移除该服务的实例,但是Eureka并不会移除,而是进入自我保护机制,将该失效的服务保护起来 首先对Eureka注册中心需要了解的是Eureka各个节点都是平等的,没有ZK中角色的概念, 即使N-1个节点挂掉也不会影响其他节点的正常运行。 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。 ##### 为什么需要自我保护机制 ##### Eureka集群和ZooKeeper集群不同,ZooKeeper集群有角色的概念,而Eureka集群中没有角色的概念;在Eureka集群中,所有节点完全平等,Eureka集群允许挂掉N-1台服务器; 基于不同的结果体系,两个集群分别提供了两种分区容错性手段 1.ZooKeeper-过半保护机制 2.Eureka集群-自我保护机制 ##### 触发条件 ##### 在 1 分钟后,Renews (last min) < Renews threshold \* 0.85 Renews threshold:Eureka Server 期望每分钟收到客户端实例续约的总数 Renews (last min):Eureka Server 最后 1 分钟收到客户端实例续约的总数 ##### 自我保护解决方式 ##### eureka: #server: #关闭自我保护机制-不推荐使用 #enable-self-preservation: false #设置自我保护机制的阈值-不推荐使用 默认是85% Renews threshold/Renews (last min)<85% #renewal-percent-threshold: 0.49 #部署多个Eureka Server变成客户端 register-with-eureka: true-推荐 #没有进入自我保护模式是会移除微服务的 85-100% ###### 注意: ###### Eureka 的自我保护模式是有意义的,该模式被激活后,它不会从注册列表中剔除因长时间没收到心跳导致租期过期的服务,而是等待修复,直到心跳恢复正常之后,它自动退出自我保护模式。这种模式旨在避免因网络分区故障导致服务不可用的问题。例如,两个客户端实例 C1 和 C2 的连通性是良好的,但是由于网络故障,C2 未能及时向 Eureka 发送心跳续约,这时候 Eureka 不能简单的将 C2 从注册表中剔除。因为如果剔除了,C1 就无法从 Eureka 服务器中获取 C2 注册的服务,但是这时候 C2 服务是可用的。 ##### Eureka集群(采用AP) ##### Eureka集群中没有角色的概念,所有Eureka服务端完全对等 任何一个EurekaClient(提供者/消费者)可以对任何一个EurekaServer进行任意读写操作,所有的写操作,EurekaServer会同步到其他EurekaServer上达到最终一致性; #### ZooKeeper和Eureka集群的区别 #### 1.Eureka没有像ZooKeeper那样的leader机制 2.Eureka支持健康检查,自我保护等,ZooKeeper有过半数存活原则 3.服务列表变更ZooKeeper服务端会有watch机制,而Eureka是长轮询机制 4.ZooKeeper是CP设计,Eureka是AP设计 #### CAP原则 #### 任何一个分布式存储系统的集群都应该遵从CAP原则,但是只能同时保证其中两个原则 一般来说分区问题是不能避免的,所以P分区容错是必选的,因此都是在A和C中选择的 ##### 详解 ##### ###### Consistency 强一致性 ###### 任何时候,访问任意一台集群节点,得到的数据都是相同的 ###### Availability 高可用性 ###### 任何时候集群都能正常对外提供服务,不考虑非正常情况 ###### Partition tolerance 分区容错性 ###### 分区问题也就是脑裂,两个服务器是正常工作的,但是因为网络等原因无法互相通信,分区容错就是对分区问题有解决方案; ###### 思考:redis集群是CP还是AP? ###### AP,如果发生脑裂,redis就会变成两个集群,那么无法保证C #### 服务的消费者-Ribbon+RestTemplate/Fegin(常用) #### 在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式: 1、ribbon+restTemplate 2、feign。 ##### Ribbon+RestTemplate ##### Ribbon简介:是一个基于HTTP和TCP客户端的负载均衡调用器,用于实现微服务之间的通信,它可以在客户端配置 ribbonServerList(服务端列表),然后轮询请求以实现均衡负载 ##### SpringCloud和Ribbon+RestTemplate消费者实现 ##### 配置application.yml server: port: 8084 eureka: client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.bbb.com:8082/eureka,http://www.ccc.com:8083/eureka instance: instance-id: consumer1 prefer-ip-address: true spring: application: name: consumer1 启动项 @EnableEurekaClient //开启ribbon @EnableDiscoveryClient @SpringBootApplication public class SpringcloudConsumerRibbonApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudConsumerRibbonApplication.class, args); } @Bean @LoadBalanced//负载均衡 LB public RestTemplate getRestTemplate(){ return new RestTemplate(); } /** * 1.切换成随机的负载均衡算法 * 2.选择一个最小并发 * BestAvailableRule * 3.根据响应时间分配权重 * WeightedResponseTimeRule * 4.在轮询的负载均衡加上重试机制 * RetryRule * @return */ @Bean public IRule getRule(){ return new RoundRobinRule(); } } Controller实现消费 @Controller @RequestMapping("/my") public class MyController { @Autowired private RestTemplate restTemplate; @RequestMapping("/test") @ResponseBody public String test(){ String result = restTemplate.getForObject("http://PROVIDER1/provider/hello/ccccc",String.class); return result; } } ##### Ribbon负载均衡和Nginx的负载均衡有什么不同? ##### Nginx是服务器端负载均衡,负载均衡的策略算法是在服务器端实现的。 Ribbon是客户端负载均衡,负载均衡算法是由调用者本身维护的 ##### 负载均衡实现 ##### @Bean @LoadBalanced//该注解代表负载均衡 public RestTemplate getRestTemplate(){ return new RestTemplate(); } ###### 注意:如果不需要负载均衡(只有一个服务提供者)也必须添加该注解 ###### ##### 负载均衡的核心IRule ##### IRule是Ribbon负载均衡算法的核心实现接口 ##### 常用实现类 ##### 1.RoundRobinRule(默认):轮询算法 2.RandomRule:随机算法 3.BestAvailableRule:选择一个最小的并发请求的server 4.WeightedResponseTimeRule:根据响应时间分配一个weight, 响应时间越长,weight越小,被选中的可能性越低。 5.RetryRule:在轮询的负载均衡策略加上重试机制。 ###### 设置负载均衡策略 ###### 在启动类中添加如下代码: @Bean public IRule getRule(){ return new RandomRule();//任意的负载均衡策略对象 } ##### 解决如果有两种服务,其中有一种服务是希望轮询而另外一种希望随机 ##### 配置两个Config配置类 public class IRuleConfig1 { @Bean public IRule getRule(){ return new RoundRobinRule(); } } public class IRuleConfig2 { @Bean public IRule getRule(){ return new RandomRule(); } } 配置启动项 @RibbonClients({@RibbonClient(name = "PROVIDER1",configuration = IRuleConfig1.class), @RibbonClient(name = "PROVIDER2",configuration = IRuleConfig2.class)}) public class SpringcloudConsumerRibbonApplication { } #### 服务消费者-Feign #### Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。 使用Feign,只需要创建一个接口并注解,它具有可插拔的注解特性。 Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。 ##### 有了Ribbon为什么还要Feign? ##### Ribbon需要结合RestTemplate对象来实现微服务调用的。 但是这种调用方式不太符合大多数程序员面向接口编程的习惯, 因此Feign本身继承了Ribbon,使用注解+接口的方式实现了服务间的调用 ##### Feign的特点 ##### Feign的特点可以简化客户端的编程,只需要提供一个接口并且结合注解就可以实现微服务的调用,对应调用者来说就好像调用本地的方法一样(有点类似于Dubbo的RPC直接操作Service) ##### SpringCloud和Feign消费者实现 ##### 配置application.yml server: port: 8085 eureka: client: service-url: defaultZone: http://www.aaa.com:8080/eureka,http://www.bbb.com:8082/eureka,http://www.ccc.com:8083/eureka instance: instance-id: consumer2 prefer-ip-address: true spring: application: name: consumer2 启动项 @EnableFeignClients @SpringBootApplication @EnableEurekaClient public class SpringcloudConsumerFeignApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudConsumerFeignApplication.class, args); } @Bean public IRule getRule(){ return new RandomRule(); } } 编写Service接口,与提供者一致,但是不需要方法体 提供者 @Controller @RequestMapping("/provider") public class HelloController { @Value("${server.port}") private String port; @RequestMapping("/hello/{param}") @ResponseBody public String hello(@PathVariable("param") String param){ return "调用了提供者" + port + "参数为:" + param; } } 接口 @FeignClient("provider1") @RequestMapping("/provider") public interface ITestService { @RequestMapping("/hello/{param}") String hello(@PathVariable("param") String param); } Controller调用 @Controller @RequestMapping("/my") public class MyController { @Autowired private ITestService testService; @RequestMapping("/test") @ResponseBody public String test(){ return testService.hello("123"); } }
还没有评论,来说两句吧...