springcloud-微服务介绍与项目搭建
目录:
一、微服务概述 二、spring cloud 技术栈三、springcloud-服务治理四、负载均衡(Ribbon)五、熔断保护(Hystrix)六、 Feign七、服务网关(Zuul) 八、总结回顾
一、微服务概述
1.1:什么是微服务
为适应企业的业务发展,提高软件研发的生产力,降低软件研发的成本,软件架构也作了升级和优化,将一个独立的系统拆分成若干个小的服务,每个小服务运行在不同的进程中,服务与服务之间采用http轻量协议传输数据,每个服务所拥有的功能具有独立性强、低耦合的特点,这样的设计就实现了单个服务的高内聚,服务与服务之间的低耦合效果,这样一个一个的小服务就是微服务,基于这种设计的系统架构即微服务架构。
1.2:微服务的优点
1.单一职责功能,每个服务都很简单,只关注于一个业务功能
2.每个微服务独立的开发,部署
3.每个单体应用不局限于固定的技术栈,开发者可以自由选择开发技术,提供API服务。
4.易于规模化开发,多个开发团队可以并行开发,每个团队负责一项服务
5.改善故障隔离。一个服务宕机不会影响其他的服务
1.3:微服务的缺点
1.增加运维成本
2.隐式接口(修改一个服务的接口,所有关联的服务都要做调整)
3.增加测试成本
4.重复劳动
5.分布式系统的复杂性
1.4:微服务系统架构图解
二、Spring Cloud技术栈
1:微服务技术栈
服务治理:服务注册、发现
负载均衡,网关路由:高可用,集群部署,校验、请求转发、服务集成
容错:避免雪崩
监控跟踪:监控资源利用、服务响应、容器资源利用情况
消息总线:消息队列、异步通信
配置管理:统一配置管理
2.2:Spring Cloud是什么
SpringCloud 为开发人员构建微服务架构提供了完整的解决方案,SpringCloud是若干个框架的集合,它包括 spring-cloud-config、spring-cloud-eureka 等近20个子项目,它提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、
分布式消息队列、配置管理等领域的解决方案。
2.3:Spring Cloud 技术栈
微服务的兴起出现了很多优秀的公司和技术:
服务治理:Dubbo(阿里巴巴)、Dubbox(当当)、Eureka(Netflix)、consul等
配置管理:Disconf(百度)、Qconf(360)、Diamood(淘宝)等。
服务跟踪:Hydra(京东)、Sleuth(Spring Cloud)等
三、服务治理(Eureka)
3.1:什么是服务治理?
微服务架构的缺点中最主要的就是由微服务数量众多导致维护成本巨大,服务治理为解决此问题而产生的。服务治理的作用是让维护人员从人工维护中解放出来,由服务自维护,微服务作为服务提供方主动向服务治理中心注册,服务消费方通过服务治理中心查询需要的服务并进行调用。
3.2:Eureka server 与 Eureka client
Spring cloud 中Eureka Server 作为服务治理的服务端, Eureka Client 作为服务
治理的客户端,客户端需要向服务端进行注册,客户端通过服务端可以发现其他
客户端并进行服务调用。
3.3:Eureka 服务治理调用过程
1.服务提供方会向Eureka server进行注册,然后每30秒(默认值)会向Eureka server发送一
次心跳告诉Eureka server“我还活着”,而如果Eureka server在90秒(默认值)没有收到
服务提供方发送的心跳则会根据配置对无效的服务进行剔除或保留。
2.服务消费方从 Eureka server中获取已注册的服务列表,然后通过负载均衡的方式进行服
务的接口调用。
3.Eureka Server可以部署多台并相互注册,达到高可用的效果。
3.4:Eureka server 代码实现
3.4.1:新建一个springboot工程并在 pom 文件中添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.4.2:配置 application 文件
server:
port: 8085 # 指定端口
spring:
application:
name: eureka-server-demo # 服务名
eureka:
server:
enable-self-preservation: false #自保护,为true时服务down掉,服务中心不会删除down掉的服务
client:
register-with-eureka: true # 是否将自己注册到Eureka服务中
fetch-registry: true # 是否从Eureka中获取注册信息
serviceUrl: # 多台 eureka 之间相互注册
defaultZone: http://localhost:8084/eureka/
3.4.3:在启动类中添加@EnableEurekaServer注解支持
package com.example.eurekaserverdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer // 添加注解支持
@SpringBootApplication
public class EurekaServerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerDemoApplication.class, args);
}
}
完成这一步之后,一个 eureka 服务治理的服务端就已经搭建完毕了。
eureka-server UI 界面:
3.5:Eureka Client 代码实现
3.5.1:添加 jar 包依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.5.2:配置 application 文件
server:
port: 8086
spring:
application:
name: eureka-client-demo
eureka:
client:
register-with-eureka: true
fetch-registry: true
serviceUrl:
defaultZone: http://localhost:8085/eureka/
3.5.3:在启动类中添加服务发现注解支持
package com.example.eurekaclientdemo01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient // 添加服务发现注解支持
@SpringBootApplication
public class EurekaClientDemo01Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClientDemo01Application.class, args);
}
}
注解使用场景:
@EnableDiscoveryClient:适用多个服务治理组件如:eureka、consul、zookeeper等
@EnableEurekaClient:只能在 eureka 作为服务治理时使用
3.6:多台 Eureka-server 的情况:
需要保持相同的服务名称 并且 eureka-server 之间相互注册,实现高可用。
启动 eureka 的服务端和客户端项目,可以看到客户端已经注册到服务端了。
四、负载均衡(Ribbon)
4.1:Ribbon 介绍
Spring Cloud Ribbon 是基于客户端的负载均衡工具,负载均衡分为服务端和客户端负载均衡。F5、LVS等就是服务端负载均衡,客服端负载均衡与服务端负载均衡的区别在于客户端要维护一份服务列表,Ribbon 从 Eureka Server 获取服务列表,
Ribbon根据负载均衡算法直接请求到具体的微服务。
4.2:Ribbon 的执行流程:
1.从 Eureka Server 中获取服务列表
2.经过 Ribbon 进行负载均衡计算(默认按照轮询)
3.将请求转发到其他服务
4.3:Ribbon 的使用
4.3.1:添加依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.1</version>
</dependency>
4.3.2:在启动类中注入 RestTemplate并添加@LoadBalanced注解
package com.example.eurekaclientdemo03;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientDemo03Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClientDemo03Application.class, args);
}
@Bean
@LoadBalanced // 从eureka server中取出服务列表,实现负载均衡。
public RestTemplate restTemplate(){
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
4.3.3:将要被调用的服务的方法
第一台服务:
package com.example.eurekaclientdemo01.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/ribbonDemo")
public class RibbonDemoController {
@PostMapping("/method")
public String method(){
System.out.println("/user-service客户端1 的 method 方法执行");
return "客户端1 的 method 方法执行";
}
}
第二台服务:
package com.example.eurekaclientdemo02.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/ribbonDemo")
public class RiboonDemoController {
@PostMapping("/method")
public String method(){
System.out.println("/user-service客户端2 的 method 方法执行");
return "客户端2 的 method 方法执行";
}
}
4.3.4:使用 RestTemplate 进行调用
1.注入 RestTemplate 实例
2.通过 RestTemplate 实例调用相应的 get/post 方法,以服务名的方式进行接口调用
package com.example.eurekaclientdemo03.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/eureka-client-demo-03")
public class RibbonDemoController {
@Autowired
private RestTemplate restTemplate;
@PostMapping("/method")
public String method(){
// 调用的服务名
String serviceId = "eureka-client-demo";
System.out.println("客户端3 的 method 方法执行");
String resultStr = restTemplate.postForObject("http://" + serviceId + "/ribbonDemo/method",
null, String.class);
System.out.println("返回数据 = " + resultStr);
return "客户端3 的 method 方法执行";
}
}
五、容错保护(Hystrix)
5.1:什么是容错保护
容错保护是指微服务在执行过程中出现错误并从错误中恢复的能力。微服务容错性不好很容易导致雪崩效应。微服务的雪崩效应表现在服务与服务之间调用,当其中一个服务无法提供服务可能导致其他服务也宕掉,比如:登录服务调用用户信息服务查询用户信息,由于用户信息服务无法服务导致登录服务一直等待,导致关联的服务在调用登录服务时都处于一直等待的状态而无法提供服务即是微服务的雪崩效应。
5.2:Spring Cloud Hystrix
Spring cloud Hystrix 是基于 Netflix 的开源框架 Hystrix 的整合,它实现了断路器、
线程隔离、信号隔离、等容错功能。
5.3:Hystrix 的使用
5.3.1:添加jar包依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
5.3.2:在启动类中添加@EnableHystrix注解支持
package com.example.eurekaclientdemo03;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
@EnableHystrix // 添加容错处理注解支持
public class EurekaClientDemo03Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClientDemo03Application.class, args);
}
@Bean
@LoadBalanced // 从eureka server中取出服务列表,实现负载均衡。
public RestTemplate restTemplate(){
return new RestTemplate(new OkHttp3ClientHttpRequestFactory());
}
}
5.3.2:在调用接口的方法上指定容错的处理方法
1.注入RestTemplate实例
2.使用restTemplate时通过服务名的方式进行调用
3.在接口调用的方法上方添加@HystrixCommand并指定容错处理方法,容错处理方法的返回
值必须和原被调用的方法返回值类型保持一致
package com.example.eurekaclientdemo03.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* Created by husz1 on 2019/3/11.
*/
@RestController
@RequestMapping("/eureka-client-demo-03")
public class RibbonDemoController {
@Autowired
private RestTemplate restTemplate;
@PostMapping("/method")
@HystrixCommand(fallbackMethod = "method2") // 指定调用接口报错之后的容错方法
public String method(){
String serviceId = "eureka-client-demo";
System.out.println("客户端3 的 method 方法执行");
String resultStr = restTemplate.postForObject("http://" + serviceId + "/ribbonDemo/method",
null, String.class);
System.out.println("返回数据 = " + resultStr);
return "客户端3 的 method 方法执行";
}
public void method2(){
System.out.println("方法调用出错了。。。");
}
}
六、Feign
6.1:Feign 的介绍
Spring Cloud Feign是基于Netflix feignFeign是开发的声明式、模板化的HTTP客户端并且整合了Spring Cloud Ribbon和Spring Cloud Hystrix,Feign可以帮助我们更快捷、优雅地调用HTTP API。
通常在企业中服务与服务之间调用一般都是使用 feign 的方式进行调用的,这样便于管理和维护,也对调用了什么系统的哪些接口一目了然。
6.2:Feign 的应用
6.2.1:引入jar包依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
6.2.2:开启容错保护支持,设置为false时,调用出错时容错方法不会执行
feign:
hystrix:
enabled: true
6.2.3:在启动类上添加@EnableFeignClients注解支持
package com.example.eurekaclientdemo01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableDiscoveryClient
@EnableFeignClients // 添加对 feign 的注解支持
@SpringBootApplication
public class EurekaClientDemo01Application {
public static void main(String[] args) {
SpringApplication.run(EurekaClientDemo01Application.class, args);
}
}
6.2.4:定义feign接口并指定容错处理的方法
1.注解 @FeignClient 中的value值为调用服务的服务名,fallbackFactory指定容错处理类
2.接口中的方法的参数列表、请求类型、请求路径返回类型要与被调用方暴露出来的接口保
持一致
3.当服务调用出错时,一般都会打印报错信息
package com.example.eurekaclientdemo01.feign;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
@Component
@FeignClient(value = "eureka-client-demo3", // 调用的服务名
fallbackFactory = FeignDemo.FeignDemoFallbackFactory.class)
public interface FeignDemo {
@PostMapping("/method")
public String method();
@Slf4j
@Component
class FeignDemoFallbackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
// 记录报错日志
if (!(throwable instanceof RuntimeException || throwable.getMessage() != null)){
log.error("eureka-client-demo3 feign fallback reason ", throwable);
}
return new FeignDemo() {
//对每个方法进行容错处理
@Override
public String method() {
return "服务调用出错。。。";
}
};
}
}
}
6.2.4:使用定义的feign接口调用
1.注入FeignDemo实例
2.通过FeignDemo的实例直接进行方法调用即可
package com.example.eurekaclientdemo01.controller;
import com.example.eurekaclientdemo01.feign.FeignDemo;
import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/ribbonDemo")
public class RibbonDemoController {
@Autowired
private FeignDemo feignDemo;
@PostMapping("/method")
public String method(){
// 通过feignDemo 接口直接调用即可
String method = feignDemo.method();
System.out.println("/user-service客户端1 的 method 方法执行");
return "客户端1 的 method 方法执行";
}
}
七、服务网关(Zuul)
7.1:什么是服务网关
服务网关是在微服务前边设置的一道屏障,请求先到服务网关,网关会对请求进行过滤、
校验、路由等处理。有了服务网关可以提高微服务的安全性,校验不通过的请求将被拒绝访
问。
前面介绍的Ribbon和Feign的负载均衡调用可以不用经过网关,因为这些调用属于微服务
与微服务内部之间的调用,而对于那些对外提供服务的微服务,比如:登录,则必须经过网
关来保证微服务的安全。
7.2:Spring Cloud Zuul 介绍
Spring Cloud Zuul 是整合 Netflix 公司的Zuul开源项目实现的微服务网关,它实现了
请求路由转发(根据不同的请求转发到不同的服务)、负载均衡、校验过滤等功能,Ribbon
属于对内的微服务而Zuul是属于对外提供的微服务。
当Spring Cloud Zuul微服务启动后就从服务治理中心获取到了所有的注册服务列表,当
请求到达Zuul时再根据配置的路由转发规则进行请求转发。
Zuul网关,不仅提供对外服务访问,微服务也可以通过zuul请求其他微服务。
Zuul 对请求转发图解:
7.3:Spring Cloud Zuul 的使用
7.3.1:引入jar包依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
7.3.2:配置路由
server:
port: 8079
spring:
application:
name: gateway-demo
zuul: # 配置路由
routes:
user-service: # 用户管理,名字是自定义的
# 配置请求 URL 的请求规则,该前缀只是用于路由转发的一个识别规则,是自定义的
# 请求经过Zuul转发时需要在请求路径上加上该前缀,直接请求微服务不需要加上前缀。
path: /user-service/** #*/
serviceId: eureka-client-demo # 服务的id
order-service: # 订单管理
path: /order-service/** #*/
serviceId: eureka-client-demo-03
eureka:
client:
register-with-eureka: true # 是否将自己注册到Eureka服务中
fetch-registry: true # 是否从Eureka中获取注册信息
serviceUrl:
defaultZone: http://localhost:8085/eureka/,http://localhost:8084/eureka/
7.3.3:定义一个filter类继承ZuulFilter,对请求进行检验拦截
1.filterType()方法指定过滤器的生命周期,包括 pre(在请求被路由之前调用)、route
(在请求被路由时调用)、post(在route和error过滤器之后被调用)、error(处理请求发
生错误时被调用)。
2.filterOrder()方法设置执行顺序,数字越大,优先级越低。
3.shouldFilter()方法设置过滤器是否开启
4.在run()方法中编写具体的校验规则,如:校验登录token,对指定请求路径放行等
package com.example.zuuldemo.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.stereotype.Component;
@Component
public class UserLoginZuulFilter extends ZuulFilter {
// 该过滤器是否执行
@Override
public boolean shouldFilter() {
return true;
}
// 编写过滤的业务逻辑
@Override
public Object run() throws ZuulException {
return null;
}
// 设置执行顺序,数字越大,优先级越低
@Override
public int filterOrder() {
return 0;
}
// 设置过滤器类型:zuul中定义了四种不同生命周期的过滤器类型
// pre:可以在请求被路由之前调用 route:在路由请求时候被调用
//post:在route和error过滤器之后被调用 error:处理请求时发生错误时被调用
@Override
public String filterType() {
return "pre";
}
}
7.3.3:在启动类中添加@EnableZuulProxy注解支持
package com.example.zuuldemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy // 添加 zuul 网关注解支持
@SpringBootApplication
public class ZuulDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulDemoApplication.class, args);
}
}
八、总结回顾
以上组件是搭建一个 springcloud 微服务系统必备的几个组件,当然也可以选择其他的组件,如服务治理可以选择 consul 等。下面我们通过下面的这张架构图来自己回顾一下 springcloud 对微服务的整套技术解决方案。