一、微服务架构
- 什么是分布式?
不同模块部署在不同服务器上。
作用:分布式解决网站高并发带来问题。 - 什么是集群?
多台服务器部署相同应用构成一个集群。
作用:通过负载均衡设备共同对外提供服务。 - 什么是RPC?
RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。
它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。
比如两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数或者方法,由于不在一个内存空间,不能直接调用,这时候需要通过就可以应用RPC框架的实现来解决。- restful、soap、rpc理解:
- RESTful是一种架构设计风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 RESTful架构或服务。
- SOAP,简单对象访问协议是一种数据交换协议规范,
是一种轻量的、简单的、基于XML的协议的规范。SOAP协议和HTTP协议一样,都是底层的通信协议,只是请求包的格式不同而已,SOAP包是XML格式的。
SOAP的消息是基于xml并封装成了符合http协议,因此,它符合任何路由器、 防火墙或代理服务器的要求。
soap可以使用任何语言来完成,只要发送正确的soap请求即可,基于soap的服务可以在任何平台无需修改即可正常使用。 - RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯)
RPC 是一个请求响应模型。客户端发起请求,服务器返回响应(类似于Http的工作方式)
RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。
- 几种比较典型的RPC的实现和调用框架 :
- RMI实现,利用java.rmi包实现,基于Java远程方法协议(Java Remote Method Protocol) 和java的原生序列化。
- Hessian,是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 基于HTTP协议,采用二进制编解码。
- thrift是一种可伸缩的跨语言服务的软件框架。thrift允许你定义一个描述文件,描述数据类型和服务接口。依据该文件,编译器方便地生成RPC客户端和服务器通信代码。
- Dubbo是阿里巴巴公司开源的一个高性能优秀的高性能、轻量级的开源Java RPC框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
- SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。
- restful、soap、rpc理解:
- 什么是SOA?
业务系统分解为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力。
通过服务的组合和编排来实现上层的业务流程。
作用:简化维护,降低整体风险,伸缩灵活。 - 什么是微服务?
架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性(单一职责,边界,异步通信,独立部署)是分布式概念的更严格执行。
作用:各服务可独立应用,组合服务也可系统应用(巨石应用[monolith]的简化实现策略-平台思想) - 使用RPC http技术实现简单会员与订单系统通讯:
- 创建会员项目SpringCloud-Member:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <dependencies> <!-- spring boot test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
package chauncy.contorller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import chauncy.entity.UserEntity; import chauncy.service.UserServcie; @RestController @RequestMapping("/userApi") public class UserApiController { @Autowired private UserServcie userService; @RequestMapping("/getUser") public UserEntity getUser(Integer userId){ return userService.getUser(userId); } }
package chauncy.service; import java.util.List; import chauncy.entity.UserEntity; public interface UserServcie { public List<UserEntity> getAllUser(); public UserEntity getUser(Integer id); }
package chauncy.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import chauncy.dao.UserDao; import chauncy.entity.UserEntity; import chauncy.service.UserServcie; @Service public class UserServiceImpl implements UserServcie{ @Autowired private UserDao userDao; @Override public List<UserEntity> getAllUser(){ return userDao.getAllUser(); } @Override public UserEntity getUser(Integer id){ List<UserEntity> userList=getAllUser(); for(UserEntity user : userList){ if(user.getId() == id){ return user; } } return null; } }
package chauncy.dao; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Component; import chauncy.entity.UserEntity; @Component public class UserDao { public List<UserEntity> getAllUser(){ List<UserEntity> userList=new ArrayList<UserEntity>(); for (int i = 0; i <= 20; i++) { userList.add(new UserEntity(i, "name:"+i)); } return userList; } }
package chauncy.entity; public class UserEntity { private Integer id; private String name; public UserEntity(Integer id, String name) { super(); this.id = id; this.name = name; } public UserEntity() { super(); } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package chauncy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args){ SpringApplication.run(App.class, args); } }
server: context-path: /member
- 创建订单项目SpringCloud-Order:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <dependencies> <!-- spring boot test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.10</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
package chauncy.controller; import java.io.IOException; import org.apache.http.HttpEntity; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class OrderController { @RequestMapping("/getOrderUserId") public String getOrderUserId(Integer userId) { String result = get("http://www.chauncywang.com:8080/member/userApi/getUser?userId="+userId); return result; } /** * 发送 get请求 */ public String get(String url) { CloseableHttpClient httpclient = HttpClients.createDefault(); try { // 创建httpget. HttpGet httpget = new HttpGet(url); System.out.println("executing request " + httpget.getURI()); // 执行get请求. CloseableHttpResponse response = httpclient.execute(httpget); try { // 获取响应实体 HttpEntity entity = response.getEntity(); System.out.println("--------------------------------------"); // 打印响应状态 System.out.println(response.getStatusLine()); if (entity != null) { // 打印响应内容长度 System.out.println("Response content length: " + entity.getContentLength()); String result = EntityUtils.toString(entity); // 打印响应内容 System.out.println("Response content: " + result); return result; } System.out.println("------------------------------------"); } finally { response.close(); } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭连接,释放资源 try { httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } }
package chauncy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
server: context-path: /order port: 8081
- 创建会员项目SpringCloud-Member:
二、SpringCloud
SpringCloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。它运行环境简单,可以在开发人员的电脑上跑。另外说明spring cloud是基于Springboot的,所以需要开发中对Springboot有一定的了解。
SpringCloud底层是基于HttpClient的,只不过做了一些封装。
三、服务的注册与发现(Eureka)
Eureka是SpringCloud内置的注册中心,其它可以做注册中心的有:Zookeeper、Redis等。
- 服务注册:
<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>
package chauncy.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class EurekaServer { public static void main(String[] args) { SpringApplication.run(EurekaServer.class, args); } }
server: port: 8888 eureka: instance: hostname: localhost client: registerWithEureka: false #是否将自身注册 fetchRegistry: false #如果为true,启动时报警 serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 服务提供者:
<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>
package chauncy.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class EurekaClientController { @Value("${server.port}") String port; @RequestMapping("/client") public String client(@RequestParam String name) { return "name:" + name + ",from port:" + port; } }
package chauncy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } }
eureka: client: serviceUrl: defaultZone: http://www.chauncywang.com:8888/eureka/ server: port: 8999 spring: application: name: SpringCloud-EurekaClient
四、服务消费者(rest+ribbon)
在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
- 什么是ribbon?
ribbon是一个负载均衡客户端,可以很好的控制http和tcp的一些行为。Feign默认集成了ribbon。 - 使用ribbon+restTemplate搭建消费者模型:
<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>
package chauncy.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import chauncy.service.RibbonService; @RestController @RequestMapping("/ribbon") public class RibbonController { @Autowired RibbonService ribbonService; @RequestMapping("/index") public String index(@RequestParam String name){ return ribbonService.indexLoad(name); } }
package chauncy.service; public interface RibbonService { public String indexLoad(String name); }
package chauncy.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import chauncy.service.RibbonService; @Service public class RibbonServiceImpl implements RibbonService{ @Autowired RestTemplate restTemplate; public String indexLoad(String name) { return restTemplate.getForObject("http://SPRINGCLOUD-EUREKACLIENT/client?name="+name, String.class); } }
package chauncy; 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; @SpringBootApplication @EnableEurekaClient public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
eureka: client: serviceUrl: defaultZone: http://www.chauncywang.com:8888/eureka/ server: port: 8666 spring: application: name: SpringCloud-Ribbon
五、服务消费者(Feign)
- 什么是Feign?
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:- Feign 采用的是基于接口的注解。
- Feign 整合了ribbon。
- 使用Feign搭建消费者模型:
<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-feign</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>
package chuancy.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import chuancy.service.FeignService; @RestController public class IndexController { @Autowired private FeignService feignService; @RequestMapping("/index") public String index(String name){ return feignService.client(name); } }
package chuancy.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient("SpringCloud-EurekaClient") public interface FeignService { @RequestMapping("/client") public String client(@RequestParam("name")String name); }
package chuancy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableFeignClients @EnableDiscoveryClient public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
eureka: client: serviceUrl: defaultZone: http://www.chauncywang.com:8888/eureka/ server: port: 8777 spring: application: name: SpringCloud-Feign
六、Hystrix断路器
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。为了解决这个问题,业界提出了断路器模型。
- 什么是Hystrix?
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。 - 在ribbon使用断路器:
改造SpringCloud-Ribbon项目- 引入Hystrix依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
- 改造service和启动类:
package chauncy.service.impl; 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; import chauncy.service.RibbonService; @Service public class RibbonServiceImpl implements RibbonService{ @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod="error") public String indexLoad(String name) { return restTemplate.getForObject("http://SPRINGCLOUD-EUREKACLIENT/client?name="+name, String.class); } public String error(String name){ return "参数为:"+name+",调用SPRINGCLOUD-EUREKACLIENT失败!"; } }
package chauncy; 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; @SpringBootApplication @EnableEurekaClient @EnableHystrix public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
- 引入Hystrix依赖:
- 在Feign中使用断路器:
Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开它,在配置文件加以下代码:
feign.hystrix.enabled=true
基于SpringCloud-Feign工程进行改造,只需要在FeignClient的FeignService接口的注解中加上fallback的指定类就行了:
FeignServiceHystrix需要实现FeignService接口,并注入到Ioc容器中,代码如下:package chuancy.service; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(value="SpringCloud-EurekaClient",fallback=FeignServiceHystrix.class) public interface FeignService { @RequestMapping("/client") public String client(@RequestParam("name")String name); }
在配置文件application.yml中增加feign.hystrix.enabled=true:package chuancy.service; import org.springframework.stereotype.Component; @Component public class FeignServiceHystrix implements FeignService{ @Override public String client(String name) { return "feign-->name:"+name+"系统错误,调用接口失败!"; } }
eureka: client: serviceUrl: defaultZone: http://www.chauncywang.com:8888/eureka/ server: port: 8777 spring: application: name: SpringCloud-Feign feign: hystrix: enabled: true
- Hystrix Dashboard (断路器:Hystrix 仪表盘)
基于SpringCloud-Ribbon改造,SpringCloud-Feign的改造相同。
首选在pom.xml引入起步依赖:
在主程序启动类中加入@EnableHystrixDashboard注解,开启hystrixDashboard:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
打开浏览器:访问http://localhost:8666/hystrix,界面如下:package chauncy; 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.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient @EnableHystrix @EnableHystrixDashboard public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
点击monitor stream,进入下一个界面,访问:
http://localhost:8666/ribbon/index?name=chauncy
此时会出现监控界面:
七、使用Zuul构建API Gateway
- 什么是API Gateway?
在Spring Cloud微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(zuul、Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。 - 什么是Zuul?
- 定义:
英:Routing in an integral part of a microservice architecture. For example, / may be mapped to your web application, /api/users is mapped to the user service and /api/shop is mapped to the shop service. Zuul is a JVM based router and server side load balancer by Netflix.
译:路由在微服务架构的一个组成部分。 例如,/可以映射到您的Web应用程序,/ api / users映射到用户服务,并且/ api / shop映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。 - 其功能包括:
- 验证
- 见解
- 压力测试
- 金丝雀测试
- 动态路由
- 服务迁移
- 减载
- 安全
- 静态响应处理
- 主动/主动流量管理
- Zuul的规则引擎允许规则和过滤器基本上用任何JVM语言编写,内置支持Java和Groovy。
- 定义:
- 搭建SpringCloud-Zuul工程:
pom.xml:
启动类App.java:<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:package chauncy; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
eureka: client: serviceUrl: defaultZone: http://www.chauncywang.com:8888/eureka/ server: port: 8678 spring: application: name: SpringCloud-Zuul zuul: routes: api-a: path: /api-a/** service-id: SpringCloud-Ribbon api-b: path: /api-b/** service-id: SpringCloud-Feign
- 服务过滤:
zuul不仅只是路由,并且还能过滤,做一些安全验证。
在上面搭建的工程中引入代码:
引入后,重启工程访问:http://localhost:8678/api-a/ribbon/index?name=chauncy,显示:token is empty。package chauncy.filter; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; /** * @classDesc: 功能描述(使用Zuul实现服务过滤) * @author: ChauncyWang * @version: 1.0 */ @Component public class MyFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(MyFilter.class); /** * filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型,具体如下: * 1.pre:路由之前。 * 2.routing:路由之时。 * 3.post: 路由之后。 * 4.error:发送错误调用。 */ @Override public String filterType() { return "pre"; } /** * filterOrder:过滤的顺序。 */ @Override public int filterOrder() { return 0; } /** * shouldFilter:这里可以写逻辑判断,是否要过滤。代码采用return true;表永远过滤。 */ @Override public boolean shouldFilter() { return true; } /** * run:过滤器的具体逻辑。可用很复杂,包括查sql、nosql去判断该请求到底有没有权限访问。 */ @Override 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 (accessToken == 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; } log.info("ok"); return null; } }
八、分布式配置中心
分布式配置中心(Spring Cloud Config),在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client。