一 .微服务架构介绍
1.单体架构是什么
单体架构也称之为单体系统或者是单体应用。就是一种把系统中所有的功能、模块耦合在一个应用中的架构方式。
2 单体架构特点 2.1 打包成一个独立的单元(导成一个唯一的 jar 包或者是 war 包) 2.2 会一个进程的方式来运行
3 单体架构优缺点
3.1 优点
项目易于管理,部署简单。
3.2 缺点
测试成本高,可伸缩性强,可靠性差,迭代困难,跨语言程,团队协作困难
4. 微服务
微服务是一种架构风格。一个大型的复杂软件应用,由一个或多个微服务组成。系统的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好的完成该任务。
4.1 架构风格
4.1.1 常见架构风格
客户端与服务端的,基于组件模型的架构(EJB),分层架构(MVC),面向服务架构(SOA)
4.2 微服务特点
4.2.1 系统是由多个服务构成
4.2.2 每个服务可以单独独立
4.2.3 每个服务之间是松耦合的。服务内部是高内聚的,外部是低耦合的。高内聚就是每个服务只关注完成一个功能
4.3 微服务的优点缺点
4.3.1优点:
测试容易,可伸缩性强,
二 .注册服务中心Eureka
2.1 先引入pom文件
2.2 在springboot启动类上加上@EnableEurekaServer来标识此工程是一个EurekaServer
2.3 配置yml文件
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false #服务注册,是否将自己注册到Eureka服务中
fetchRegistry: false #服务发现,是否从Eureka中获取注册信息
serviceUrl: #Eureka客户端与Eureka服务端的交互地址,高可用状态配置对方的地址,单机状态配置自己(如果不配置则默认本机8761端口)
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #是否开启自我保护模式
spring:
application:
name: eurka-server #指定服务名
三 . 创建服务提供者(Eureka Client)
当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除。
步骤如注册服务中心,但是注解不同:@EnableEurekaClient
yml配置文件
server:
port: 8762
spring:
application:
name: service-hi
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name 。
四 . 服务消费者(Feigin)
4.1 Feigin简介
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
-
Feign 采用的是基于接口的注解
-
Feign 整合了ribbon,具有负载均衡的能力
-
整合了Hystrix,具有熔断的能力
4.2 Feigin简单使用
4.2.1 添加pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4.2.2 在启动类上添加@EnableFeignClients开启Feign
4.2.3 定义Feigin接口
使用@FeignClient注解来声明Feign Client接口,该注解的value为需要远程调用的服务,而configuration指向负载均衡策略配置类 而该接口下的“provide”方法通过Feign来调用“PRODUCER”服务的“/provide”接口
//PRODUCER:指向要访问的服务
//configuration = FooConfiguration.class: Feign Client配置类,指定负载均衡策略
@FeignClient(value = "PRODUCER",configuration = FooConfiguration.class)
public interface MyFeignClient {
//当此方法别调用会自动请求到 PRODUCER服务的 /provide 资源
@RequestMapping(value = "/provide")
public String provide(@RequestParam("name") String name);
}
五 . 客户端负载均衡Ribbon
5.1 什么是负载均衡
负载均衡,英文名称为Load Balance,其意思就是将负载分摊到多个操作单元上进行执行,即把请求分担到多个服务进行执行,简而言之就是当一个服务承担不住请求压力,那我可以多弄几个服务来分担,这些服务的职责是一致的。就好比超市收银台,当客户太多,一个收银台忙不过来那我可以多增加几个收银台来减少收银慢的压力,这就是负载均衡。 5.2 什么是Ribbon
负载均衡分为服务器端负载均衡和客户端负载均衡,服务器端负载均衡如:“nginx”,而Ribbon是一个实现了客户端负载均衡组件,它和RestTemplate结合或者和Feign结合可以很好地控制HTTP和TCP客户端的行为。它从EurekaServer拉取服务列表实现负载均衡,并实现了多种策略(算法):轮询,随机等 5.3 使用RestTemplate 和 Ribbon实现负载均衡
5.3.1 在服务提供者的pom文件添加ribbon依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
5.3.2 在定义RestTemplate的方法上加上@LoadBalanced注解,该注解赋予了RestTemplate负载均衡的能力
@SpringBootApplication
@EnableEurekaClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(Consumer1Application.class, args);
}
//通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
//RestTemplate是spring内置的http请求封装
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.3.3 自定义负载均衡算法-编码方式
Ribbon默认使用的负载均衡算法为轮循,负载算法有很多,SpringCloud实现的负载均衡算法如下:
![](https://i-blog.csdnimg.cn/blog_migrate/44888465851be720f859f8d37f780e0d.png)
BaseAvailableRule:选择请求最小的服务
RoundRobinRule:使用轮询的方式依次选择每个服务进行访问
WeightedResponseTimeRule:对RoundRobinRule的扩展,可以根据服务的运行情况来增加权重,并根据权重挑选实例。
RandomRule:从服务实例列表中数据选择一个服务进行访问
RetryRule:实现了重试机制的轮询选择
Spring Cloud允许通过使用@RibbonClient声明自定义的负载均衡算法配置,@RibbonClient通过 name属性指定要调用的服务,通过configuration属性去指定该服务使用什么样的负载均衡算法配置,下面我们就来通过代码的方式实现自定义。 5.3.3.1在主程序类上打上 @RibbonClient(name = "PRODUCER", configuration = FooConfiguration.class) 标签。
@SpringBootApplication
@EnableEurekaClient
//@RibbonClient可以去指定使用什么样的负载均衡算法去调用某个服务,
//而负载均衡算法就通过configuration指向的配FooConfiguration去指定
//RibbonClient : name指向要调用的提供者服务 PRODUCER,
//configuration指向自定义的负载均衡算法配置类 FooConfiguration
@RibbonClient(name = "PRODUCER", configuration = FooConfiguration.class)
public class Consumer1Application {
public static void main(String[] args) {
SpringApplication.run(Consumer1Application.class, args);
}
//通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
//RestTemplate是spring内置的http请求封装
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
5.3.3.2.定义 FooConfiguration,实现自定义负载均衡规则,特别注意 : 该类不能被放在打有@ComponentScan标签或者@SpringBootApplication标签的类的包及其子包中,因为该类不能被ComponetScan给扫描到,否则将会覆盖掉其他所有的RibbonClient,所以请给该类额外分一个包
@Configuration
public class FooConfiguration {
//通过方法返回的实例类型指定负载均衡算法规则
@Bean
public IRule ribbonRule(IClientConfig config) {
//RandomRule : 负载均衡规则:随机
//RoundRobinRule: 轮询
//WeightedResponseTimeRule:加权
return new RandomRule(); //负载均衡算法:随机
}
}
5.3.4 自定义负载均衡算法-配置方式
PRODUCER: #提供者服务ID
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡规则-随机
六 . 熔断器Hystrix
6.1 为什么需要熔断器
我们知道我们的项目会被分成很多的服务,而每个服务是独立运行的,服务和服务之间通过REST API 实现远程相互调用,既然是远程调用就有可能会因为网络故障,或被调用的服务本身出现问题而造成服务调用延迟,调用失败等问题,而这些问题可能造成调用方对外部服务也调用失败,从而造成一些列连锁反应,甚至造成整个应用瘫痪。 例如:服务A调用服务B,而服务B调用服务C,如果服务B调用服务C出现故障,那么服务A调用服务B可能也会出现连锁反应,那么整个应用可能都会出现问题。而这种效果也被叫做雪崩效应。
6.2 什么是熔断器Hystrix
熔断器就好比家里电路装置的“保险”,当电路断路,那么“保险”装置的“保险丝”就会被熔断,从而达到断开电路的效果,以防止不良后果。而Spring Cloud Hystrix 实现了熔断器,线程隔离等一些列的服务保护功能,当Hystrix判定请求出现故障,会立马对请求做出响应动作,不会继续执行正常请求逻辑,请求线程也不会处于阻塞状态,从而有效防止雪崩效应。
6.3 实现基于Ribbon使用熔断器
6.3.1 在consumer导入pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
6.3.2 主程序打上@EnableHystrix标签,开启熔断器功能
/**
* @EnableHystrix: 开启 hystrix ,开启熔断器功能
*/
@EnableHystrix
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
//RestTemplate是spring内置的http请求封装
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
6.3.3 在ConsumerController中的方法上打上注解 @HystrixCommand注解,开启熔断器功能
//HystrixCommand:给方法启用熔断器功能,当出现访问故障,自动调用 fallbackMethod 指向的方法
@RequestMapping("/consumer")
@HystrixCommand(fallbackMethod = "errorMethod")
public String consumer(@RequestParam("name") String name){
String result = restTemplate.getForObject("http://PRODUCER/provide?name="+name,String.class);
return result;
}
public String errorMethod(String name){
return "你好,"+name+",访问错误";
}
6.4 实现基于Feigin使用熔断器
6.4.1 基于Consumer项目,配置文件开启熔断器功能
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/ #服务地址
server:
port: 3333
spring:
application:
name: consumer1
#开启熔断器
feign:
hystrix:
enabled: true
6.4.2 MyFeignClient接口开启熔断器功能,给@FeignClient标签添加fallback属性指定熔断器逻辑处理类
//PRODUCER:指向要访问的服务
//configuration = FooConfiguration.class Feign Client配置类,指定负载均衡策略
//fallback = MyFeignClientImpl.class: MyFeignClient接口的实现类,需要复写 provide方法,并实现错误处理逻辑
//当访问出现故障,程序会自动调用实现类的 provide方法的错误处理逻辑。
@FeignClient(value = "PRODUCER",configuration = FooConfiguration.class,fallback = MyFeignClientImpl.class)
public interface MyFeignClient {
//当此方法别调用会自动请求到 PRODUCER服务的 /provide 资源
@RequestMapping(value = "/provide")
public String provide(@RequestParam("name") String name);
}
@Component
public class MyFeignClientImpl implements MyFeignClient {
@Override
public String provide(String name) {
return "你好,"+name+",访问出现错误";
}
}
七 . Hystrix Dashboard
7.1 什么是Hystrix Dashboard
从上一章节中我们知道,为了防止服务实例因为故障出现整个应用崩塌的情况而出现了熔断器模型。而 Hystrix Dashboard(仪表盘)则是用来监控Hystrix的熔断器状况的一个组件,它提供了数据监控,和友好的图形化展示界面,能让使用者很好的监控和分析熔断器的状态。 7.2 简单使用Hystrix Dashboard
7.2.1 添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
7.2.2 在主程序上打上 @EnableHystrixDashboard标签,开启Hystrix Dashboard 功能
@EnableHystrix
@EnableHystrixDashboard //开启 Hystrix Dashboard (断路器:Hystrix 仪表盘)
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class ConsumerApplication {
7.2.3 在主程序类中配置Bean,注册 /hystrix.stream 仪表盘访问地址对应的Servlet:HystrixMetricsStreamServlet
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
八 .API网关 Netflix Zuul
8.1 什么是Netflix Zuul
API网关就像整个系统的门面一样,所有的外部访问都经过它实现调度,过滤,请求路由,负载均衡,校验等等,在SpringCloud为我们提供了基于Netflix Zuul实现的API网关组件——Spring Cloud Netflix Zuul,加入网关API后的服务架构如图:
![](https://i-blog.csdnimg.cn/blog_migrate/ac2b91d95f3c390aece9fdf3429b24bb.png)
8.2 实现API网关
8.2.1 搭建项目ZuulServer pom.xml核心依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.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>
<spring-cloud.version>Finchley.M9</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</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>${spring-cloud.version}</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>
8.2.2 找到主程序类打上@EnableZuulServer标签开启zuul功能
//@EnableZuulServer :开启路由网关功能
@SpringBootApplication
@EnableZuulProxy
public class ServiceZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceZuulApplication.class, args);
}
}
8.3.3 添加yml文件
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1111/eureka/ #注册中心地址
server:
port: 1113 //当前zuul服务端口
spring:
application:
name: service-zuul #服务名
zuul:
routes: #配置路由
api-a:
path: /api-a/**
serviceId: consumer1 #api-a 开头的请求跳转到 consumer1服务
api-b:
path: /api-b/**
serviceId: consumer2 #api-b 开头的请求跳转到 consumer2服务
8.3 服务过滤
Netflix Zuul功能很强大,上面我们只是演示了他的路由功能,我们接下来玩一下过滤功能,对于服务接口的请求我们要求带上相应的验证信息
8.3.1 定义类 MyFilter 继承 ZuulFilter,实现请求过滤
/**
服务过滤
*/
@Component
public class MyFilter extends ZuulFilter {
/**
返回过滤器的类型,过滤器类型如下:
pre:请求路由之前调用过滤
routing:请求routing之时调用过滤
post: 请求路由之后调用过滤
error:发送错误时调用过滤
*/
@Override
public String filterType() {
return "pre";
}
//filterOrder:过滤的顺序
@Override
public int filterOrder() {
return 0;
}
//shouldFilter:是否要过滤,true表示永远过滤。
@Override
public boolean shouldFilter() {
return true;
}
//过滤逻辑可以在该方法中处理有没有权限访问等
@Override
public Object run() {
//获取请求对象
RequestContext ctx = RequestContext.getCurrentContext();
Object pass = ctx.getRequest().getParameter("pass");
if(pass == null) {
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("pass is empty");
}catch (Exception e){}
}
return null;
}
}
重启 ZuulServer 服务,访问: http://localhost:1113/api-a/consumer?name=haha 你会看到 “pass is empty” 因为我们的请求中是没有带入 pass,那说明我们的请求过滤成功,重新访问: http://localhost:1113/api-a/consumer?name=haha&pass=111 成功的看到“haha:你好呀这里是Producer服务” 。