【学习】024 springCloud
单点系统架构
传统项目架构
传统项目分为三层架构,将业务逻辑层、数据库访问层、控制层放入在一个项目中。
优点:适合于个人或者小团队开发,不适合大团队开发。
分布式项目架构
根据业务需求进行拆分成N个子系统,多个子系统相互协作才能完成业务流程子系统之间通讯使用RPC远程通讯技术。
优点:
1.把模块拆分,使用接口通信,降低模块之间的耦合度。
2.把项目拆分成若干个子项目,不同的团队负责不同的子项目。
3.增加功能时只需要再增加一个子项目,调用其它系统的接口就可以。
4.可以灵活的进行分布式部署。
有优点就有缺点,缺点如下:
1.系统之间交互需要使用远程通信,接口开发增加工作量。
2.各个模块有一些通用的业务逻辑无法共用。
为了解决上面分布式架构的缺点,我们引入了soa架构,SOA:Service Oriented Architecture面向服务的架构。也就是把工程拆分成服务层、表现层两个工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
什么是项目集群
多台服务器部署相同应用构成一个集群
作用:通过负载均衡设备共同对外提供服务
RPC远程调用
RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。
比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决
restful、soap、rpc
(1)restful是一种架构设计风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 RESTful架构或服务。
(2)soap象访问协议是一种数据交换协议规范,
是一种轻量的、简单的、基于XML的协议的规范。SOAP协议和HTTP协议一样,都是底层的通信协议,只是请求包的格式不同而已,SOAP包是XML格式的。
soap
基于xml并封装成了符合http协议,因此,它符合任何路由器、 防火墙或代理服务器的要求。
soap可以使用任何语言来完成,只要发送正确的soap请求即可,基于soap的服务可以在任何平台无需修改即可正常使用。
(3)RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
rpc远程调用框架
几种比较典型的RPC的实现和调用框架。
(1)RMI实现,利用java.rmi包实现,基于Java远程方法协议(Java Remote Method Protocol)
和java的原生序列化。
(2)Hessian,是一个轻量级的remoting
onhttp工具,使用简单的方法提供了RMI的功能。 基于HTTP协议,采用二进制编解码。
(3)thrift是一种可伸缩的跨语言服务的软件框架。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。
(4)SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。
(4) Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。
面向于服务架构
什么是SOA
业务系统分解为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力
通过服务的组合和编排来实现上层的业务流程
作用:简化维护,降低整体风险,伸缩灵活
微服务架构
什么是微服务架构
架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的跟严格执行
SOA到微服务架构的演进过程
作用:各服务可独立应用,组合服务也可系统应用(巨石应用[monolith]的简化实现策略-平台思想)
SOA架构与微服务架构区别
SOA架构主要针对企业级、采用ESB服务(ESB企业服务总线),非常重,需要序列化和反序列化,采用XML格式传输。
微服务架构主要互联网公司,轻量级、小巧,独立运行,基于Http+Rest+JSON格式传输。
ESB也可以说是传统中间件技术与XML、Web服务等技术相互结合的产物。
SpringCloud
SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、负载均衡、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于Springboot的,所以需要开发中对Springboot有一定的了解,如果不了解的话可以看蚂蚁课堂SpringBoot课程。
服务提供者与消费关系
服务提供者:提供服务被人调用
消费者:调用被人服务
服务的注册与发现(Eureka )
在这里,我们需要用的的组件上Spring Cloud Netflix的Eureka ,eureka是一个服务注册和发现模块。
什么是Eureka
官方的介绍在这里Eureka wiki。Eureka是Netflix开源的一个RESTful服务,主要用于服务的注册发现。Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。
在我看来,Eureka的吸引力来源于以下几点:
开源:大家可以对实现一探究竟,甚至修改源码。
可靠:经过Netflix多年的生产环境考验,使用应该比较靠谱省心
功能齐全:不但提供了完整的注册发现服务,还有Ribbon等可以配合使用的服务。
基于Java:对于Java程序员来说,使用起来,心里比较有底。
spring cloud可以使用Spring Cloud, 与Eureka进行了很好的集成,使用起来非常方便。
实现服务注册
创建EureKaserver 项目
Maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <!--eureka server --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <!-- spring boot test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
配置application.yml
server: port: 8888 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动EurekaServer
package com.hongmoshui.eureka; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaApp { public static void main(String[] args) { SpringApplication.run(EurekaApp.class, args); } }
打开eureka server 界面的
http://localhost:8761 ,界面如下:
No application available 没有服务被发现 ……^_^
因为没有注册服务当然不可能有服务被发现了。
实现案例订单服务调用会员服务查询用户信息
服务提供者
创建一个服务提供者 会员服务工程 (eurekaMember),提供会员查询服务信息
创建项目service-member
Maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
application.yml配置
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8762 spring: application: name: service-member
服务接口
package com.hongmoshui.controller; import java.util.ArrayList; import java.util.List; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MemberController { @RequestMapping("/getUserList") public List<String> getUserList() { List<String> listUser = new ArrayList<String>(); listUser.add("zhangsan"); listUser.add("lisi"); listUser.add("hongmoshui"); return listUser; } }
发布服务
通过注解@EnableEurekaClient 表明自己是一个eurekaclient。
package com.hongmoshui.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class MemberApp { public static void main(String[] args) { SpringApplication.run(MemberApp.class, args); } }
演示效果
需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name 。
启动工程,打开127.0.0.1:8761 ,即eureka
server 的网址:
你会发现一个服务已经注册在服务中了,服务名为SERVICE-HI ,端口为7862
这时打开 http://127.0.0.1:8762/getUserList ,你会在浏览器上看到 :
["zhangsan","lisi","hongmoshui"]
服务消费者
创建项目sercice-order
Maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
application.yml配置
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8764 spring: application: name: service-order
编写service,调用service-member
package com.hongmoshui.service; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @SuppressWarnings("unchecked") @Service public class OrderService { @Autowired RestTemplate restTemplate; public List<String> getOrderByUserList() { return restTemplate.getForObject("http://service-member/getUserList", List.class); } }
编写controller
package com.hongmoshui.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import com.hongmoshui.service.OrderService; @RestController public class OrderController { @Autowired OrderService orderService; @RequestMapping(value = "/getOrderByUserList", method = RequestMethod.GET) public List<String> getOrderByUserList() { return orderService.getOrderByUserList(); } }
启动serviceOrder
package com.hongmoshui; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableEurekaClient @SpringBootApplication public class OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
演示效果
在工程的启动类中,通过@EnableDiscoveryClient向服务中心注册;并且向程序的ioc注入一个bean: restTemplate;并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
使用ribbon实现负载均衡
启动两个会员服务工程,端口号分别为8762、8763,订单服务 使用负载均衡策略轮训到会员服务接口。
什么是ribbon
ribbon是一个负载均衡客户端 类似nginx反向代理,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
修改会员服务工程代码区分端口项目(启动多个member项目,且其端口号不能重复,调用feign时,默认"轮询"的方式调用)
@Value("${server.port}") private String serverPort; @RequestMapping("/getUserList") public List<String> getUserList() { List<String> listUser = new ArrayList<String>(); listUser.add("zhangsan"); listUser.add("lisi"); listUser.add("hongmoshui"); listUser.add("端口号:" + serverPort); return listUser; }
开启ribbon
@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
服务消费者(Feign)
什么是Feign
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
- Feign 采用的是基于接口的注解
- Feign 整合了ribbon
创建service-order-feign工程
Maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
application.yml配置
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8765 spring: application: name: service-order-feign
编写service,调用service-member
package com.hongmoshui.service; import java.util.List; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; @FeignClient("service-member") public interface OrderFeignService { @RequestMapping("/getUserList") public List<String> getOrderByUserList(); }
@FeignClient 需要调用服务名称,@RequestMapping服务请求名称
编写controller
package com.hongmoshui.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.hongmoshui.service.OrderFeignService; @RestController public class OrderFeignController { @Autowired OrderFeignService orderFeignService; @RequestMapping("/getOrderByUserList") public List<String> getOrderByUserList() { return orderFeignService.getOrderByUserList(); } }
演示效果
package com.hongmoshui; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class OrderFeignApp { public static void main(String[] args) { SpringApplication.run(OrderFeignApp.class, args); } }
路由网关(zuul)
什么是网关
Zuul的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务。zuul默认和Ribbon结合实现了负载均衡的功能, 类似于nginx转发。
搭建SpringCloud网关
创建工程service-zuul
Maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
application.yml配置
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8769 spring: application: name: service-zuul zuul: routes: api-a: path: /api-member/** service-id: service-member api-b: path: /api-order/** service-id: service-order
发送请求http://127.0.0.1:8769/api-member/getMemberAll
转发到http://127.0.0.1:8762/getMemberAll
开启网关 @EnableZuulProxy
服务过滤
package com.hongmoshui.zuul; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; @Component public class MyFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(MyFilter.class); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } public boolean shouldFilter() { return true; } public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getParameter("token"); if (!StringUtils.isEmpty(accessToken)) { return null; } log.warn("token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); try { ctx.getResponse().getWriter().write("token is empty"); } catch (Exception e) { } return null; } }
服务启动
package com.hongmoshui.zuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy public class ZuulApp { public static void main(String[] args) { SpringApplication.run(ZuulApp.class, args); } }
服务测试
如果请求参数中没有传入token参数 直接返回报错信息
传入token参数可以正常访问请求
断路器(Hystrix)
为什么需要 Hystrix?
在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用(RPC)。为了保证其高可用,单个服务又必须集群部署。由于网络原因或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会出现网络延迟,此时若有大量的网络涌入,会形成任务累计,导致服务瘫痪,甚至导致服务“雪崩”。为了解决这个问题,就出现断路器模型。
什么是服务雪崩
分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选择.
服务雪崩应对策略
针对造成服务雪崩的不同原因, 可以使用不同的应对策略:
- 流量控制
- 改进缓存模式
- 服务自动扩容
- 服务调用者降级服务
流量控制 的具体措施包括:
- 网关限流
- 用户交互限流
- 关闭重试
Hystrix作用
服务的降级
什么是服务降级
所有的RPC技术里面服务降级是一个最为重要的话题,所谓的降级指的是当服务的提供方不可使用的时候,程序不会出现异常,而会出现本地的操作调
service-order工程新增Maven依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
Rest方式使用断路器
service类的改造【在getOrderByUserList方法上,增加注解@HystrixCommand(fallbackMethod = "orderError"),并在类中增加orderError方法】:
package com.hongmoshui.service; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @SuppressWarnings("unchecked") @Service public class OrderService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "orderError") public List<String> getOrderByUserList() { int i = 1 / 0; return restTemplate.getForObject("http://service-member/getUserList", List.class); } public List<String> orderError() { List<String> listUser = new ArrayList<String>(); listUser.add("not orderUser list"); return listUser; } }
启动类的改造【增加注解:@EnableHystrix】:
package com.hongmoshui; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableEurekaClient @SpringBootApplication @EnableHystrix public class OrderApp { public static void main(String[] args) { SpringApplication.run(OrderApp.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
注:
@HystrixCommand 作用:服务发生错误,回调方法。
@EnableHystrix 启动断路器
Fegin使用断路器
改造service-order-feign工程
配置文件新增:
feign:
hystrix:
enabled: true
新增一个实现类,实现OrderFeignService接口【其方法的实现,就是接口调用失败时的回调方法】
package com.hongmoshui.service.impl; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Component; import com.hongmoshui.service.OrderFeignService; @Component public class OrderFeignServiceImpl implements OrderFeignService { public List<String> getOrderByUserList() { List<String> listUser = new ArrayList<String>(); listUser.add("not orderUser list"); return listUser; } }
OrderFeignService接口的@FeignClient注解上,增加回调参数【fallback值就是其实现类】
package com.hongmoshui.service; import java.util.List; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import com.hongmoshui.service.impl.OrderFeignServiceImpl; @FeignClient(value = "service-member", fallback = OrderFeignServiceImpl.class) public interface OrderFeignService { @RequestMapping("/getUserList") public List<String> getOrderByUserList(); }
分布式配置中心
什么是配置中心
在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。
创建git地址
- 使用github地址 https://github.com/hongmoshui
- config-dev.properties ---dev环境
- 上传配置文件
#配置文件的内容 userName=hongmoshui
创建config-server项目
pom文件依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
application.properties 配置文件
spring.application.name=config-server server.port=8889 spring.cloud.config.server.git.uri=https://github.com/hongmoshui/maven-web-ver2.0 spring.cloud.config.server.git.searchPaths=src/main/resources/configurationCenter spring.cloud.config.label=master spring.cloud.config.server.git.username=hongmoshui spring.cloud.config.server.git.password=
配置文件注释:
spring.cloud.config.server.git.uri:配置git仓库地址
spring.cloud.config.server.git.searchPaths:配置仓库路径
spring.cloud.config.label:配置仓库的分支
spring.cloud.config.server.git.username:访问git仓库的用户名
spring.cloud.config.server.git.password:访问git仓库的用户密码
在远程configurationCenter仓库中,配置了3个配置文件
config-dev.properties:配置有两个属性:name=hongmoshui-dev,nickName=yuelang-dev
config-pro.properties:配置有两个属性:name=hongmoshui-pro,nickName=yuelang-pro
config-test.properties:配置有两个属性:name=hongmoshui-test,nickName=yuelang-test
在服务端获取文件配置时,其URL与配置文件的映射关系如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
上面的url会映射{application}-{profile}.properties
对应的配置文件,{label}
对应git上不同的分支,默认为master。
启动config-server项目
package com.hongmoshui; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
创建config-client项目
maven依赖
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RC1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
bootstrap.properties
spring.application.name=config server.port=8881 spring.cloud.config.label=master spring.cloud.config.profile=dev spring.cloud.config.uri= http://localhost:8889/
- spring.application.name 指明配置文件名
- spring.cloud.config.label 指明远程仓库的分支
- spring.cloud.config.profile
- dev开发环境配置文件
- test测试环境
- pro正式环境
- spring.cloud.config.uri= http://localhost:8889/ 指明配置服务中心的网址。
URL与配置文件的映射关系如下:
- /{application}/{profile}[/{label}]
- /{application}-{profile}.yml
- /{label}/{application}-{profile}.yml
- /{application}-{profile}.properties
- /{label}/{application}-{profile}.properties
上面的url会映射{application}-{profile}.properties
对应的配置文件,{label}
对应git上不同的分支,默认为master。
即:上面的配置,会映射到config-dev.properties文件,然后读取其中的配置信息,存储在本服务内存中。
如果远程仓库的配置文件信息修改了,要同步到本服务,需要重新启动服务