SpringCloud是一个微服务框架,用于搭建分布式应用。
SpringCloud是在SpringBoot的基础上,集成了多种技术,是一系列技术的集合,为微服务提供了一站式解决方案。
因为是建立在SpringBoot的基础上,所以具有SpringBoot的所有特点。
SpringCloud的特点:
- 约定优于配置
- 开箱即用
- 部署环境多样,可以部署到PC Server、Docker、云端。
- 组件轻量级,功能全面
- 选型中立,并不是一定要用自带的组件,因为有spring强大的整合能力,也可以使用其它组件,比如服务发现不一定要用自带的Eureka,也可以用Zookeeper,但SpringCloud对Eureka的支持最好。
SpringCloud的技术栈 | 组件 | 全家桶:
- Spring Cloud Eureka 服务治理
- Spring Cloud Ribbon 负载均衡
- Spring Cloud Hystrix 断路保护
- Spring Cloud Zuul 网关(API Gateway)
- Spring Cloud Config、Bus、Sleuth (可选)
SpringCloud的组件都是轻量级的。
前4个对于SpringCloud微服务都是必需的,第5项列出的是可选的,根据需求选择。
Eureka、Ribbon、Hystrix都是netflix的开源项目,SpringCloud都将它们集成了。
SpringCloud的版本
其它框架的版本都是1.x,2.x这样命名走的,SpringCloud的版本是Axx、Bxx、Cxx这样命名走的。
ga是稳定版;snapshot是正在开发的版本,不稳定。
服务调用的demo
比如要查询某用户的所有订单 => 在用户服务中访问订单服务。
订单服务
@Controller @RequestMapping("/order") public class OrderController { //根据user_id查询某用户的所有订单 @GetMapping("/user/{user_id}") @ResponseBody public ListqueryOrdersByUserId(@PathVariable Integer user_id){ //进过一系列操作,返回订单列表 //..... return orderList; } //..... }
方法返回值要返回给用户服务,需要@ResponseBody转换为json格式,不能String、List、Map、视图什么的直接传过去。
被调者本身可能要调用视图来显示,或者直接返回某种类型的数据,不返回json格式的数据,那被调者需要重新写一个方法来作为服务接口,返回json格式,供其他服务调用。
就算双方都要使用视图来响应浏览器,也不能在2个服务之间直接传递页面,可以把被调者的视图(模板引擎什么的)copy过来,让被调者以json格式返回视图名。
用户服务
@Controller @RequestMapping("/user") public class UserController { //springboot的服务之间使用REST相互访问、调用,springboot已经把REST封装为RestTemplate类,注入使用即可 private RestTemplate restTemplate; @Autowired public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @GetMapping("/{user_id}/order") @ResponseBody public ListqueryOrdersByUserId(@PathVariable Integer user_id){ //订单服务的地址 String url = "http://localhost:8080/order/user/{user_id}"; //参数:服务地址、期待返回的数据类型(class对象)、要传入的参数 List orderList = restTemplate.getForObject(url, List.class ,user_id); return orderList; } //..... }
restTemplate.getForObject(url, targetClass,Object...args); //参数可变
参数指的是url中的参数,比如:
String url = "http://localhost:8080/springboot/user/{group}/{user_id}";
restTemplate.getForObject(url, targetClass,实参1,实参2) //第n个实参对应url中的第n个{ }
我们看到IDEA提示:不能自动装配,因为找不打RestTemplate类型的实例。
我们需要手动创建一个RestTempalte的实例:
@SpringBootApplication public class App{ // 通过指定的方法,自动创建Bean(RestTempalte)的一个实例,放到spring容器中 @Bean public RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(App.class); } }
在引导类中创建,可供此服务的所有类使用。
上面把服务的url写死在代码中,当然也可以写在application.yml中,使用@Value注入:
server: user: url: http://localhost:8080/springboot/order/user/{user_id}
@Value("${server.user.url}") private String url;
虽然好修改url一些,但节点地址依然是死的、固定的,只会访问指定的节点,做负载均衡、或者服务节点增减时怎么办?
传统的方式是使用nginx做代理、负载均衡,由nginx决定分发到该服务的哪个节点。
可服务之间有大量的调用关系,总不能在有调用关系的2个服务之间都加上nginx吧:
访问量大的时候,比如购物网站 =>xx狂欢节,需要加服务节点,之后要把加的节点撤下来,这都要修改nginx上的节点信息,很麻烦。
微服务使用服务治理组件:
服务治理组件有2个功能:服务注册、服务发现。
SpringCloud使用Eureka来做服务治理组件,如果服务节点很多,也需要做Eureka的集群。
服务注册:
服务的各节点将节点信息(提供的服务、本机地址)注册到Eureka上,并通过心跳实时更新本节点的信息。
所谓心跳,是指节点每几十秒就向服务发现组件发送一段消息,像心跳一样稳定、有规律,表示该节点还在工作(活着);如果一个节点连续几次都没发送心跳,心不跳了,说明这个节点挂了,Eureka会自动删除该节点的信息。
服务发现:
可查询Eureka来获取(发现)提供该服务的所有节点的信息。
Eureka只是登记好服务节点的信息,并不知道要调用哪个节点来处理,还需要使用Ribbon来做负载均衡
比如服务A的a1节点要调用服务C:
(1)a1节点向Ribbon发送负载均衡请求
(2)Ribbon查询Eureka,拿到提供服务C的所有节点的信息表,根据指定的算法(比如轮询、加权轮询、随机等)来确定要使用c1节点
(3)Ribbon将c1节点的信息(ip)返回给a1节点
(4)a1节点根据ip向c1节点发起请求,调用服务C
feign
feign用于向服务提供者传递大量参数,自带Ribbon负载均衡的功能。
微服务容错
1、雪崩效应
服务往往存在嵌套调用,比如服务A调用服务B,服务B要调用服务C:
服务A -> 服务B -> 服务C
如果C出现什么问题,B一直阻塞着等C搞好;A一直阻塞着等B搞好,即雪崩效应。
请求越积越多,负载越来越大,,最终导致服务A、B、C都不可用。
2、容错
容错:当依赖出现问题时,对系统的影响程度,影响大——容错低,影响小——容错高。
提升微服务容错有3种方式:
- 增加节点数量
这是通用的。
- 为请求设置超时
多少秒内没有完成,就直接返回出错提示,这个请求就算处理完了,开始处理下一个请求。
- 设置断路器
对某个服务的请求,一段时间内,失败个数达到指定值或者失败率达到指定值,就会熔断链路,返回我们自定义的数据(比如错误提示、空值等)。
熔断只是半熔断,断路器是半开状态,会放行一小部分对此服务的请求进行检测,如果请求成功率达到指定值,就认为此服务已被修复,会重新打开链路。