SpringCloud01
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
共分四天进行学习
一、微服务基础知识
1.1 项目架构的演变
项目架构的演变:单体应用架构→垂直应用架构→分布式架构→微服务架构
分布式架构的缺点:
- 抽取服务的粒度较大
- 服务提供方与调用接口方耦合度较高
为了解决以上缺点,微服务架构被提出。
微服务的优点
- 通过服务的原子化拆分,以及微服务的独立打包、部署和升级,小团队的服务周期将缩短,运维成本也将大幅下降
- 微服务遵循单一原则。微服务之间采用Restful等轻量协议传输
微服务的缺点:
- 微服务过多,服务治理成本高,不利于系统维护
- 分布式系统开发的技术成本高(容错、分布式事务等)
1.2核心概念
1.2.1远程调用技术
比较流行的远程调用技术:
RPC:RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 下图为RPC的过程。
HTTP:发出请求,得到响应,比较简单。
HTTP与RPC进行比较
1.2.2CAP原理
1.3 常见的微服务框架
1.3.1 SpringCloud介绍
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
1.3.2 通过RestTemplate远程调用服务
- 1、创建商品服务
使用RestTemplate进行服务之间的远程调用,首先创建商品服务product_service,按照MVC设计模式,使用SpringBoot创建一个简单的能够完成增删改查功能的项目,代码比较简单就不再写了。
- 2 创建订单服务,调用商品服务
在工程内创建订单服务,跨模块调用商品服务。此处使用HTTP协议进项远程调用。步骤如下:
- 在订单服务的启动器类中创建RestTemplate 对象,用来访问商品服务。代码如下:
/*** 创建RestTemplate对象,交给IOC容器管理,用来访问product_service* @param args*/@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
- 在订单的controller中调用商品服务。代码如下:
package com.runze.order.controller;import com.runze.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate RestTemplate restTemplate;/*** 参数:商品ID* 通过订单系统,调用商品服务根据商品id查询商品信息* 1、需要配置商品对象* 2、需要调用商品服务**/@GetMapping("/buy/{id}")public Product findProductById(@PathVariable Long id){Product product = null;product=restTemplate.getForObject("http://127.0.0.1:9001/product/"+id,Product.class);return product;}
}
如上,商品就做为一个的小型的微服务供其他服务调用,但是这种微服务有很多弊端,如下图:
所以,我们使用SpringCloud来创建微服务项目。
二 SpringCloud框架
SpringCloud中常见的组件介绍
2.1 注册中心
注册中心的作用如下图
常见的注册中心有以下四种
2.1.1 Eureka介绍
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。
SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。其工作流程如下图所示:
使用步骤
- 搭建eureka server
1.1 创建工程
1.2 导入坐标
<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies>
1.3 配置application.yml
server:port: 9000eureka:instance:hostname: localhostclient:register-with-eureka: false #是否将自己注册到注册中心fetch-registry: false #是否从eureka中获取注册信息service-url: #暴露给eureka.Client的请求地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
1.4 配置启动类
package com.runze.eureka;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication
//激活eureka Server
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class,args);}
}
- 将服务提供者注册到eureka Server上
2.1 引入EurekaClient的坐标
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netfix-eureka-client</artifactId></dependency>
2.2 修改application.yml添加EurekaClient的信息
eureka:client:service-url: #暴露给eureka.Client的请求地址defaultZone: http://localhost:9000/eureka/
2.3修改启动类,添加服务发现的支持(可选)
- 服务消费者通过注册中心获取服务列表并调用
3.1 引入EurekaClient的坐标
3.2 修改application.yml添加EurekaClient的信息
3.3修改启动类,添加服务发现的支持(可选)
这三步同上,不写了
4.3 获取注册中心中服务提供者的信息
package com.runze.order.controller;import com.runze.order.domain.Product;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.List;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate DiscoveryClient discoveryClient;/*** 参数:商品ID* 通过订单系统,调用商品服务根据商品id查询商品信息* 1、需要配置商品对象* 2、需要调用商品服务**/@GetMapping("/buy/{id}")public Product findProductById(@PathVariable Long id){//从eureka service中获取服务提供者的信息List<ServiceInstance> instances = discoveryClient.getInstances("service-product");ServiceInstance serviceInstance = instances.get(0);//无需将服务提供方的url写死,随用随改。Product product=restTemplate.getForObject("http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/product/"+id,Product.class);return product;}
}
2.1.2 高可用的引入,Eureka Server之间相互注册
##### 2.1.3 Eureka的细节问题
- 在Eureka的注册中心中显示服务提供者的ip地址
eureka:client:service-url: #暴露给eureka.Client的请求地址defaultZone: http://localhost:9000/eureka/instance:prefer-ip-address: true #使用ip注册instance:instance-id: ${spring.cloud.client.ip-address}:${server.port} #设置Eureka中显示服务提供者的ip地址
- 设置续约到期的时间以及发送心跳的间隔
instance:#设置Eureka中显示服务提供者的ip地址instance-id: ${spring.cloud.client.ip-address}:${server.port}# 设置心跳的间隔lease-renewal-interval-in-seconds: 5# 设置续约到期的时间lease-expiration-duration-in-seconds: 10
- Eureka的自我保护机制
eureka:instance:hostname: localhostclient:register-with-eureka: false #是否将自己注册到注册中心fetch-registry: false #是否从eureka中获取注册信息service-url: #暴露给eureka.Client的请求地址defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/server:enable-self-preservation: false #关闭自我保护机制eviction-interval-timer-in-ms: 4000 #测试阶段设置剔除服务间隔
2与3测试阶段可用,服务上线后不建议使用
2.1.4 Eureka源码详解
- Spring Boot的自动装载,使用一个小案例演示
package com.runze.damain;import lombok.Data;@Data
public class User {private String name;private Integer id;
}
package com.runze.damain;import org.springframework.context.annotation.Bean;public class UserConfiguration {@Beanpublic User getUser(){User user=new User();user.setName("李华");user.setId(1);return user;}
}
package com.runze.damain;import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;public class UserImportSelector implements ImportSelector{@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {return new String[]{UserConfiguration.class.getName()};}
}
package com.runze.damain;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(UserImportSelector.class)
public @interface EnableUserBean {}
package com.runze.test;import com.runze.damain.EnableUserBean;
import com.runze.damain.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;@EnableUserBean
public class TestEnableUserBean {public static void main(String[] args) {/***@ClassName: -->EnableUserBean--> UserImportSelector--> UserConfiguration--> User*@Param*@Author 李润泽*@Return*@Time*/AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(TestEnableUserBean.class);User bean = ac.getBean(User.class);System.out.println(bean);}
}
运行流程:测试类–>EnableUserBean–> UserImportSelector–> UserConfiguration–> User
2. Eureka的启动流程
2.2 Ribbon
2.2.1Ribbon概述
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的。所以,对Spring Cloud Ribbon的理解和使用,对于我们使用Spring Cloud来构建微服务非常重要
2.2.2 Ribbon功能
1.服务调用
- 在RestTemplate上加上注解 @LoadBalanced
- 使用服务名代替IP地址
@GetMapping("/buy/{id}")public Product findProductById(@PathVariable Long id){Product product=restTemplate.getForObject("http://service_product/product/1",Product.class);return product;}
2.负载均衡
Ribbon是一个典型的客户端负载均衡器,Ribbon会获取服务的所有地址,根据其内部的负载均衡算法,获取本次请求的有效地址。Ribbon的负载均衡策略如下:
3. ribbon源码分析
2.3 Consul
consul是google开源的一个使用go语言开发的服务发现、配置管理中心服务。内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。服务部署简单,只有一个可运行的二进制的包。每个节点都需要运行agent,他有两种运行模式server和client。每个数据中心官方建议需要3或5个server节点以保证数据安全,同时保证server-leader的选举能够正确的进
Consul与Eureka的区别与联系
启动Consul
2.3.1 Consul入门
导入maven坐标
<!--springcloud 提供的对基于consul的服务发现--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency><!--actuator的健康检查--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency>
服务提供方的配置
cloud:consul:host: 127.0.0.1 #consul服务器的主机地址port: 8500 #consul服务器的ip地址discovery:#是否需要注册register: true#注册的实例ID (唯一标志)instance-id: ${spring.application.name}-1#服务的名称service-name: ${spring.application.name}#服务的请求端口port: ${server.port}#指定开启ip地址注册prefer-ip-address: true#当前服务的请求ipip-address: ${spring.cloud.client.ip-address}
服务消费者的配置
cloud:consul:host: 127.0.0.1 #consul服务器的主机地址port: 8500 #consul服务器的ip地址discovery:#是否需要注册register: true#注册的实例ID (唯一标志)instance-id: ${spring.application.name}-1#服务的名称service-name: ${spring.application.name}#服务的请求端口port: ${server.port}#指定开启ip地址注册prefer-ip-address: true#当前服务的请求ipip-address: ${spring.cloud.client.ip-address}
在服务消费者的启动器上@LoadBalanced,Ribbon的负载均衡支持
@SpringBootApplication
@EntityScan("cn.runze.order.entity")
public class OrderApplication {/*** springcloud对consul进行了进一步的处理* 向其中集成了ribbon的支持*/@LoadBalanced@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}public static void main(String[] args) {SpringApplication.run(OrderApplication.class,args);}
}
在调用服务的地方直接写服务名称即可调用
package cn.runze.order.controller;import cn.runze.order.entity.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate RestTemplate restTemplate;@RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)public Product findById(@PathVariable Long id) {Product product = restTemplate.getForObject("http://service-product/product/1",Product.class);;return product;}}
2.3.2 Consul高可用集群
暂时没法演示
2.3 Feign
Feign是Netflix开发的声明式,模板化的HTTP客户端,其灵感来自Retrofit,JAXRS-2.0以及WebSocket.
- Feign可帮助我们更加便捷,优雅的调用HTTP API。
- 在SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
- SpringCloud对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka,
从而让Feign的使用更加方便。
2.3.1 Feign入门
- 导入依赖
<!--在消费者的pom文件上添加feign依赖--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>
- 配置调用接口
package com.runze.order.feign;import com.runze.order.domain.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;/*** 生命需要调用的微服务名称* @FeignClient* *name:调用的服务的名称*/@FeignClient("service-product")
@Component
public interface ProductFeignClient {/*** 配置需要调用的微服务接口*/@RequestMapping(value="/product/{id}",method= RequestMethod.GET)public Product findById(@PathVariable("id") Long id);}
- 在启动类上激活feign
@SpringBootApplication
@EntityScan("com.runze.order.domain")
@EnableEurekaClient
@EnableFeignClients
- 通过自动的接口调用远程微服务
package com.runze.order.controller;import com.runze.order.domain.Product;
import com.runze.order.feign.ProductFeignClient;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.List;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate ProductFeignClient feignClient;//使用Ribbon进行远程调用@GetMapping("/buy/{id}")public Product findProductById(@PathVariable Long id){Product product = feignClient.findById(id);return product;}
2.3.2 Feign的负载均衡
以轮询的方式进行访问服务提供者
2.3.3 feign配置日志输出
在服务调用者的配置文件中配置如下内容即可
# 配置feign的日志输出
# 日志级别NONE:不输出日志 BASIC:适用于生产环境追踪问题 HEADERS:在BASIC的基础上,记录请求和响应头问题 FULL:记录所有
feign:client:config:service-product:loggerlevel: FULL
logging:level:com.runze.order.feign.ProductFeignClient: debug
2.3.3 feign源码分析
2.4高并发问题
2.4.1使用JMeter模拟高并发情况
如何解决由于请求积压造成的服务崩溃问题:服务隔离的方式,具体如下图
服务隔离分为两类:
- 线程池隔离:就是对多个服务单独创建线程池,防止由于某个服务访问量过多导致其他服务无法使用的问题。
- 信号量隔离:实际就是一个计数器,设置某个服务的最大访问量,如果超出这个阈值,就会直接报错,无法访问。
2.4.2使用线程池隔离解决某一服务访问量过大的问题
- 导入坐标
<dependency><groupId>com.netflix.hystrix</groupId><artifactId>hystrix-metrics-event-stream</artifactId><version>1.5.12</version></dependency><dependency><groupId>com.netflix.hystrix</groupId><artifactId>hystrix-javanica</artifactId><version>1.5.12</version></dependency>
2.创建OrderCommand类,设置线程池参数
package com.runze.order.command;import com.netflix.hystrix.*;
import com.runze.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.client.RestTemplate;public class OrderCommand extends HystrixCommand<Product> {private RestTemplate restTemplate;private Long id;public OrderCommand(RestTemplate restTemplate, Long id) {super(setter());this.restTemplate = restTemplate;this.id = id;}private static Setter setter() {// 服务分组HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("order_product");// 服务标识HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("product");// 线程池名称HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("order_product_pool");/*** 线程池配置* withCoreSize : 线程池大小为10* withKeepAliveTimeMinutes: 线程存活时间15秒* withQueueSizeRejectionThreshold :队列等待的阈值为100,超过100执行拒绝策略*/HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter().withCoreSize(50).withKeepAliveTimeMinutes(15).withQueueSizeRejectionThreshold(100);// 命令属性配置Hystrix 开启超时HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()// 采用线程池方式实现服务隔离.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)// 禁止.withExecutionTimeoutEnabled(false);return Setter.withGroupKey(groupKey).andCommandKey(commandKey).andThreadPoolKey(threadPoolKey).andThreadPoolPropertiesDefaults(threadPoolProperties).andCommandPropertiesDefaults(commandProperties);}@Overrideprotected Product run() throws Exception {System.out.println(Thread.currentThread().getName());return restTemplate.getForObject("http://service-product/product/1",Product.class);}@Overrideprotected Product getFallback(){Product product=new Product();product.setProductName("不好意思,出错了");return product;
}
}
3.在OrderController中调用OrderCommand的方法
System.out.println(Thread.currentThread().getName());return new OrderCommand(restTemplate, id).execute();
4.测试,使用JMeter模拟过量访问findProductById,同时在浏览器端访问Order服务的另一个方法findById,会发现访问findProductById与访问findById的不是同一线程
SpringCloud01相关推荐
- SpringCloud学习笔记day01
SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构.这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务 ...
- springCloud之Netflix完整学习
SpringCloud 是一个生态,不是一门技术,用来解决下面的问题 微服务的4个核心问题: 服务很多,客户如何访问? 这么多服务,服务之间怎么通信? 服务太多了,如何治理? 服务挂了怎么办? 解决方 ...
- Spring Cloud学习资料01
SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构.这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务 ...
- 微服务01SpringCloud Eureka Ribbon Nacos Feign Gateway服务网关
微服务技术栈导学 SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构.这些架构之间有怎样的差别呢? 1.0.学 ...
- 关于SpringCloud,我肝了7万字
目录 概述 一.业务场景介绍 微服务cloud整体聚合父工程Project 父工程的pom 子模块 微服务提供者支付模块:cloud-provider-payment8001 微服务消费者订单模块cl ...
- Hystrix 集群 及 集群监控 Turbine
Hystrix 集群 及 集群监控 turbine Hystrix 集群及监控 turbine Feign.Hystrix整合 集群后超时设置 本章知识: 1.Hystrix集群及监控turbine ...
- SpringCloud学习笔记01——Eureka 和 Nacos注册
SpringCloud01 1.认识微服务 随着互联网行业的发展,对服务的要求也越来越高,服务架构也从单体架构逐渐演变为现在流行的微服务架构.这些架构之间有怎样的差别呢? 1.0.学习目标 了解微服务 ...
最新文章
- 操作系统结构-外核结构
- 实现控制台上的进度条
- python连接mongodb进行查询_Python中的MongoDB基本操作:连接、查询实例
- Java中单链表的实现
- java中的数据结构总结
- 国开mysql答案_国开MySQL数据库应用形考任务.doc
- 父类可以调用子类的方法吗_python类的继承、多继承及查找方法顺序
- 前端学习(3246):react的生命周期getSnap
- 马斯克:特斯拉智能召唤功能已被使用超过55万次
- php有没有dao层,php框架开发四(DAO层)_PHP教程
- Python批处理MODIS数据并计算NDVI
- 电阻、电容、电感、半导体器件的失效分析
- 基本知识 100151
- Mode首席执行官Paul Dawes:从销售工程师到科技领导者
- 数据库expecting ''', found 'EOF'异常——原载于我的百度空间
- 在ios10+的safair中实现视频的自动播放
- 【DP】【高精】WZK打雪仗
- 华三comware跳槽_小灰的网工日常之华三瘦转胖AP,我与华三的又一段孽缘呀~~~
- react 函数组件暴露方法
- 圣诞节营销攻略之贺卡、逼单、开发的模板