SpringCloud微服务入门
一.什么是SpringCloud
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、智能路由、消息总线、负载均衡、断路器、数据监控等。Spring Cloud 并不重复造轮子,而是将市面上开发得比较好的模块集成进去,进行封装,从而减少了各模块的开发成本。
二.SpringCloud的基础功能
- 服务发现:Netflix Eureka
- 客户端负载均衡:Netflix Ribbon
- 断路器:Netflix Hystrix
- 服务网关:Netflix Zuul
- 声明式服务调用:Netflix Feign
- 分布式配置:Spring Cloud Config
三.SpringCloud引入Eureka
Spirng Cloud Eureka使用Netflix Eureka来实现服务注册与发现,由两个组件组成:Eureka服务端(Eureka Service)和Eureka客户端(Eureka Client)。而Eureka Client又分为服务提供者和服务消费者。
-
在项目中加入Eureka服务中心的依赖
<!-- Eureka服务中心 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
-
写yml配置文件
server: port: 7070 spring: application: name: eureka-service eureka: client: service-url: #该注册中心指向另一个注册中心 互相注册。可配置多个 使用“,”分割 defaultZone: http://127.0.0.1:7070/eureka #是否检索服务 fetch-registry: false #是否向服务注册中心注册自己 register-with-eureka: false instance: #开启后显示服务器地址 prefer-ip-address: true ip-address: 127.0.0.1
-
在主启动类上加注解
@SpringBootApplication @EnableEurekaServer //将项目作为SpringCloud中的注册中心 public class EurekaMain7070 { public static void main(String[] args) { SpringApplication.run(EurekaMain7070.class,args); } }
-
将其他服务注册到Eureka注册中心
<!-- Eureka客户中心 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
server: port: 8080 spring: application: name: user-service eureka: client: #表示向注册中心注册自己 默认为true register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetch-registry: true service-url: defaultZone: http://127.0.0.1:7070/eureka #入驻地址
@SpringBootApplication @EnableEurekaClient //让注册中心能够发现 public class UserMain8080 { public static void main(String[] args) { SpringApplication.run(UserMain8080.class,args); } }
3.1 Eureka的治理机制
- 服务提供者
- 服务注册:启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息
- 服务续约:在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka Server: "我还活着 ”
- 服务下线:当服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求给Eureka Server, 告诉服务注册中心:“我要下线了 ”
- 服务消费者
- 获取服务:当我们启动服务消费者的时候,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单
- 服务调用:服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。在进行服务调用的时候,优先访问同处一个Zone中的服务提供方。
- 服务注册中心
- 失效剔除:默认每隔一段时间(默认为60秒) 将当前清单中超时(默认为90秒)没有续约的服务剔除出去
- 自我保护:EurekaServer 在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%(通常由于网络不稳定导致)。 Eureka Server会将当前的实例注册信息保护起来, 让这些实例不会过期,尽可能保护这些注册信息
四.SpringCloud引入Ribbon
Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。它不像spring cloud服务注册中心、配置中心、API网关那样独立部署,但是它几乎存在于每个spring cloud 微服务中。包括feign提供的声明式服务调用也是基于该Ribbon实现的。ribbon提供很多种负载均衡算法(默认的负载均衡策略是轮询),例如 轮询、随机、最少并发策略等等。甚至包含自定义的负载均衡算法。
-
在项目中加入Ribbon依赖
<!-- ribbon负载均衡 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
-
在yml配置文件中配置Ribbon负载均衡策略
provider-service: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
-
在主启动类开启负载均衡
@Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }
-
进行测试
@RestController @RequestMapping("user") public class UserController { @Resource private UserService userService; @GetMapping("getUserById/{id}") public User getUserById(@PathVariable("id") Integer id){ return userService.getUserById(id); } }
@Service public class UserServiceImpl implements UserService { @Resource private RestTemplate restTemplate; @Override public User getUserById(Integer id) { String url = "http://provider-service/user/getUserById/"; return restTemplate.getForObject(url+id,User.class); } }
多次刷新看到调用的端口在来回切换则成功实现负载均衡
五.SpringCloud引入Hystrix
在高并发的情况下,由于单个服务的延迟,可能导致所有的请求都处于延迟状态,甚至在几秒钟就使服务处于负载饱和的状态,资源耗尽,直到不可用,最终导致这个分布式系统都不可用,这就是“雪崩”。Hystrix断路器可以防止一个应用程序多次试图执行一个操作,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝), 向调用方返回一个错误响应, 而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延
-
在消费者pom中加入Hystrix的依赖包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
在主启动类加入注解
@EnableHystrix
-
测试是否生效
@Override @HystrixCommand(fallbackMethod = "queryUserByIdFallback", //失败后的回调函数 //每十次请求中失败率达到百分之三十则开启熔断,此时再调用该服务,则直接返回失败,直到十秒后重新检测该触发条件,判断是否把熔断器打开或者继续关闭 commandProperties = { @HystrixProperty(name="circuitBreaker.requestVolumeThreshold" ,value="10"), @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds" ,value="10000"), @HystrixProperty(name="circuitBreaker.errorThresholdPercentage" ,value="30") }) public User queryUserById(Integer id) { long begin = System.currentTimeMillis(); User user= restTemplate.getForObject(url+id, User.class); long end = System.currentTimeMillis(); logger.info("访问用时{}",end-begin); return user; } private User queryUserByIdFallback(Integer id){ User user = new User(); user.setId(id); user.setUserCode("查询用户信息出现异常"); return user; }
六.SpringCloud引入Feign
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
-
在服务调用者pom加入Feign依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在yml写配置文件
feign: hystrix: enabled: true #开启Feign Hystrix 支持 httpclient: enabled: false #关闭httpclient okhttp: enabled: true #开启okHttp
-
在主启动类加入注解
@EnableFeignClients
-
使用Feign实现远程调用
/** * value:指定调用哪个服务 * fallbackFactory:熔断器的降级提示 */ @FeignClient(value = "provider-service",fallback = UserFeignClientFallback.class) public interface UserFeignClient { //采用Feign我们可以使用SpringMVC的注解来对服务进行绑定! @GetMapping("/user/getUserById/{id}") User userFeign(@PathVariable("id") Integer id); }
/** * Feign中使用断路器 * 这里主要是处理异常出错的情况(降级/熔断时服务不可用,fallback就会找到这里来) */ @Component public class UserFeignClientFallback implements UserFeignClient { @Override public User userFeign(Integer id) { User user = new User(); user.setId(id); user.setUserCode("查询用户信息出现异常"); return user; } }
@RestController @RequestMapping("user") public class UserController { @Resource private UserFeignClient userFeignClient; @GetMapping("feign/{id}") public User userFeign(@PathVariable("id") Integer id){ return userFeignClient.userFeign(id); } }
七.SpringCloud引入Zuul
Zuul是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul相当于是设备和Netflix流应用的 Web 网站后端所有请求的前门。Zuul和Eureka进行整合,将Zuul自身注册为Eureka服务治理下的应用,同时从Eureka中获得其他微服务的消息,也即以后的访问微服务都是通过Zuul跳转后获得
Zuul包含了对请求的路由和过滤两个最主要的功能:路由转发:接收一切外界请求,转发到后端的微服务上去。过滤器:在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成,路由转发也是通过过滤器实现的
-
新建项目在pom中加入依赖
<!-- Zuul网关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!-- Eureka客户中心 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
编写yml配置文件
server: port: 10080 spring: application: #服务的名称 name: zuul-service eureka: client: service-url: defaultZone: http://127.0.0.1:9090/eureka #指定注册中心地址 zuul: retryable: true ignored-services: - provider-service #忽略整个服务,对外提供接口
-
编写主启动类
@SpringBootApplication @EnableZuulProxy public class ZuulMain10080 { public static void main(String[] args) { SpringApplication.run(ZuulMain10080.class); } }
-
自定义过滤器
@Component public class LoginFilter extends ZuulFilter { /** * 返回字符串,代表过滤器的类型。包含以下4种: * pre:请求在被路由之前执行 * routing:在路由请求时调用 * post:在routing和error过滤器之后调用 * error:处理请求时发生错误调用 */ @Override public String filterType() { return "pre"; } //通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。 @Override public int filterOrder() { return 0; } //返回一个Boolean值,判断该过滤器是否需要执行。返回true执行,返回false不执行。 @Override public boolean shouldFilter() { return true; } //过滤器的具体业务逻辑。 @Override public Object run() throws ZuulException { // 登录校验逻辑。 // 1)获取Zuul提供的请求上下文对象 RequestContext ctx = RequestContext.getCurrentContext(); // 2) 从上下文中获取request对象 HttpServletRequest req = ctx.getRequest(); // 3) 从请求中获取token String token = req.getParameter("access-token"); // 4) 判断 if(token == null || "".equals(token.trim())){ // 没有token,登录校验失败,拦截 ctx.setSendZuulResponse(false); // 返回401状态码。也可以考虑重定向到登录页。 ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); } // 校验通过,可以考虑把用户信息放入上下文,继续向后执行 return null; } }
八.SpringCloud Config
随着业务的扩展,我们的服务会越来越多,越来越多。每个服务都有自己的配置文件。既然是配置文件,给我们配置的东西,那难免会有些改动的。
Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。
简单来说,使用Spring Cloud Config就是将配置文件放到统一的位置管理(比如GitHub),客户端通过接口去获取这些配置文件。在GitHub上修改了某个配置文件,应用加载的就是修改后的配置文件