*** springcloud相关maven依赖介绍:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>*********</artifactId>
</dependency>
spring-cloud-starter-parent 具备spring-boot-starter-parent同样功能并附加Spring Cloud的依赖
spring-cloud-starter-config 默认的配置服务依赖,快速自动引入服务的方式,端口8888
spring-cloud-config-server/client 用户自定义配置服务的服务端/客户端依赖
spring-cloud-starter-eureka-server 服务发现的Eureka Server依赖
spring-cloud-starter-eureka 服务发现的Eureka客户端依赖
spring-cloud-starter-hystrix/zuul/feign/ribbon 断路器(Hystrix),智能路有(Zuul),客户端负载均衡(Ribbon)的依赖
angular-ui-router 页面分发路由依赖
*** springcloud相关使用注解:
@EnableEurekaServer ->代表该启动类是一个eureka server端,也就是注册中心
@EnableEurekaClient ->代表该启动类是一个eureka client端,也就是服务生产者/消费者
@EnableDiscoveryClient ->代表该服务是一个可被发现的服务提供者
@LoadBalanced -> 代表ribbon的负载均衡开启
@RibbonClient -> 代表自定义ribbon的负载均衡规则机制
@HystrixCommand(fallbackMethod = "deptGetById_GET") -> 服务熔断机制,并指定熔断处理方法
@EnableCircuitBreaker -> 表示某客户端开启服务熔断机制
***相关概念介绍 :
- restTempl调用:springcloud是基于rest风格进行调用的微服务框架,所以,我们可以通过搭建一个生产者工程,提供增删改查的API接口,在消费者中,通过注入restTemplate的bean的方式,调用生产者启动的API接口,来完成消费者与生产者之间的相互调用(并没有使用到eurake注册中心)restTemplate是一个spring提供的用于访问客户端模板工具类
- lombok工具的使用: 能够对实体类自动生成get、set等方法,通过注解的方式完成编译
目录
3. actuator与注册中心微服务信息完善(spring-boot-starter-actuator(健康监控)配置和使用)
5. 服务的发现Discovery(对外暴露微服务的信息,提供API接口)
一:微服务简介
1. 什么是微服务?
拆分系统,一个模块一个微服务,之间通过HTTP的restful风格进行互相调用。
2. 微服务的优缺点?
优点:降低了系统之间的耦合、增加了系统的高并发可用、减少维护成本、增加了代码的复用性。
缺点:增加了资源使用成本...
3. 微服务技术栈
一个分布式的微服务架构,应该是由多种维度(技术)组合搭配而成的一个完整的架构,都有哪些维度组成?
服务开发技术 | spring、springboot、springMVC... |
服务配置与管理 | Netflix公司的Archaius、阿里的diamond... |
服务注册与发现(z注册中心) | Eureka、zookeeper、consul... |
服务调用(服务之间通讯方式) | restFul风格、RPC、GRPC |
服务熔断器(容灾处理) | Hystrix、Envoy等 |
负载均衡 | Ribbon、nginx等 |
服务接口调用(消费者调用生产者的方式) | Feign(基于ribbon封装)等 |
消息队列 | kafka、rabbitMQ、activeMQ等 |
服务配置中心 | SpringCloudConfig、Chef等 |
服务路由网关(通过配置路由访问服务) | Zuul等 |
服务监控中心 | zabbix、nagios、metrics、spectator等 |
全链路追踪(追踪服务之间互相调用等情况) | zipkin、brave、dapper等 |
服务部署 | Docket、openStack、kubernetes |
事件消息总线 | Spring Cloud Bus |
数据流操作开发包 | Spring Cloud Stream |
springcloud微服务架构,是一整套的微服务解决方案,通过各个不同的维度功能技术,将一个有一个的springboot服务工程连接起来,互相调用,从而形成带有集群的分布式服务系统。
二:eureka注册中心
1. 创建注册中心工程
1.1 引入pom文件
<!--eurekaServer注册中心所需要的依赖jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
1.2 创建application配置文件
1.2.1. 设置端口号
1.2.2. 设置eureka的服务地址以及是否发现该服务
1.2.3. 设置注册中心地址
application.yml文件配置:
server:
port: 8001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #false表示不想注册中心注册自己
fetch-registry: false #false表示自己就是注册中心,不需要去检索注册中心上的服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eurekaServer/ #设置客户端(消费者与生产者)进行注册和查询服务的地址
1.3 通过@EnableEurekaServer在启动类中标志该服务是一个注册中心,完成启动
2. eureka注册中心集群设置
设置多个eureka集群,使服务架构变的更加高可用,能够更好的适应高并发的场景,本例设置三个注册中心集群8001/8002/8003
设置方法过程:
2.1 创建多个eureka子工程,并copy启动类和配置文件。主要修改:
在8001端口的eurekayml文件中:添加其他集群注册中心地址
eureka.client.service-url.defaultZone:
http://localhost:8002/eureka/,http://localhost:8003/eureka/
在8002端口的eurekayml文件中:添加其他集群注册中心地址
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8003/eureka/
在8003端口的eurekayml文件中:添加其他集群注册中心地址
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8002/eureka/
2.2 客户端访问eureka时。同样defaultZone注册中心地址设置为多个:
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8002/eureka/,http://localhost:8003/eureka/
2.3 注册中心集群设置完成,此时发布一个服务后,三个注册中心均会被注册进去。
3. eureka与zookeeper注册中心的区别
3.1 两者之间最大的区别:eureka是AP(可用性,分区容错性)、zookeeper是CP(数据强一致性,分区容错性)
3.2 分布式架构的设计都围绕着一个原则:C(一致性)A(可用性)P(分区容错性)
三:eurekaClient:(服务注册与发现)
本文将创建一个7001/7002/7003端口的服务提供项目为客户端提供服务,三个端口均为相同项目,设置为不同端口意为将该服务设置为集群。
1. 引入主要pom文件
<!--表示该服务是eureka client的客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2. 创建application配置文件
2.1 设置端口;
2.2 申明注册中心地址
2.3 业务所需配置(mybatis/jdbc等信息)
2.4 同样的配置创建三个项目,设置不同端口号,设置不同的eureka.instance.instance-id
server:
port: 7001
#mybatis的配置文件地址、实体类、映射文件mapper路径
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.gugui.PO
mapper-locations: classpath:mybatis/mapper/**/*.xml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/spring_cloud?characterEncoding=UTF-8
username: root
password: root
driver-class-name: org.gjt.mm.mysql.Driver
type: com.alibaba.druid.pool.DruidDataSource
dbcp:
dbcp2:
min-idle: 5
max-total: 5
initial-size: 5
max-wait-millis: 2000
application:
name: producer-dept #服务名
eureka:
client:
service-url:
defaultZone: http://www:8001/eureka/
#注册到eureka上时,该服务显示的别名
instance:
instance-id: produck-7001
3. actuator与注册中心微服务信息完善(spring-boot-starter-actuator(健康监控)配置和使用)
本步骤设置该服务在注册中心上的别名以及点击该服务别名后,显示服务提供者的IP以及端口等详细信息,有助于在同一个注册中心,注册了N个服务和集群,方便区分方便定位
在只设置了instance-id时,注册中心截图如下:
3.1 打开在注册中心显示服务本身IP及端口
eureka.client.instance.prefer-ip-address=true。此时点击服务名,即可显示IP
3.2 如何点击服务名,进入详情页?
在不设置时,点击服务名进去会报错。此时就需要(健康监控)配置和使用相关内容了
添加jar包:
<!--spring-boot-starter-actuator(健康监控)配置和使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在client微服务的yml文件中,添加info信息:
#注册中心页面访问时,点击微服务连接后进行显示的信息
info:
appName: produck-7001
companyName: kangce
buildArtifactId: $project.artifactId$
version: $project.version$
其中$project.artifactId$可以通过maven中pom文件设置build规则设置读取
此时,点击注册中心的链接后,会以json形式显示这些信息
4. eureka的自我保护机制
如果服务在更换 名称或者长时间没有访问时,注册中心页面就会出现如此报错,不用担心,并不是真正的报错了,而是eureka的自我保护机制生效了。并不会影响使用。
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,
让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,
对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。
我们在单机测试的时候很容易满足心跳失败比例在 15 分钟之内低于 85%,这个时候就会触发 Eureka 的保护机制,
一旦开启了保护机制,则服务注册中心维护的服务实例就不是那么准确了,此时我们可以使用eureka.server.enable-self-preservation=false(在注册中心服务eureka-server的yml文件中)来关闭保护机制,这样可以确保注册中心中不可用的实例被及时的剔除(不推荐)。
当eureka在检测服务时,如果超过了心跳检测下限,就会启动自我保护机制,不会强制下线连接失败或者超时的服务,设计原则就是:宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。
5. 服务的发现Discovery(对外暴露微服务的信息,提供API接口)
主要使用工具类:DiscoveryClient(org.springframework.cloud.client.discovery.DiscoveryClient)
作用:作用在服务提供者中,表示该 服务可以被发现,调用者可以获取该服务的任何详细信息
用法:
5.1 在需要 被发现的服务中,创建服务发现接口,通过DiscoveryClient来将服务的信息进行接口返回:
@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
public Object discovery() {
// 获取注册中心中所有的服务名
List<String> allList = discoveryClient.getServices();
//获取服务名称是PRODUCER_7001的所有微服务集合
List<ServiceInstance> list = discoveryClient.getInstances("PRODUCER_7001");
for (ServiceInstance ser : list) {
System.out.println(ser.getHost() + " " + ser.getServiceId() + " " + ser.getPort() + " " + ser.getUri());
}
return this.discoveryClient;
}
5.2 接口写好之后,在需要被发现的服务启动类中,添加注解:@EnableDiscoveryClient
5.3 访问该服务的该地址,即可访问该服务的详细信息
介绍完springcloud的注册中心创建以及服务提供者的创建之后,就需要进行服务调用了。
两种服务调用的方式:ribbon与feign
①:ribbon是通过ribbon+restTemplate的方式完成服务的调用,可通过注册中心直接使用【服务名】+【接口地址】进行访问微服务,类似于面向服务编程,缺点是如果一个服务被多出调用,则我们还需要将这种方式进行进一步自定义封装,
②:而feign则直接设置成了面向接口的方式进行微服务的访问,解决了这一问题。③:ribbon:ribbon+restTempalte
feign:接口+注解
四:ribbon负载均衡
源码地址:https://github.com/Netflix/ribbon
1. 负载均衡LB(Load Balance)简介:
ribbon是基于Netflix Ribbon 实现的一套【客户端】的【负载均衡工具】
1.1 通过一定的负载均衡算法(简单轮询等),来完成服务的调用;
1.2 负载均衡的方式:
集中式LB:硬件的方式,在客户端和服务端之间,通过硬件按照一定的策略完成转发,从而达到负载均衡: 客户端 -> 硬件(F5等) -> 服务端
进程内LB:将负载均衡逻辑算法集成在消费方,有消费方自主决定调用什么服务。ribbon就是属于进程内LB。
2. ribbon初步配置:
2.1 修改pom文件添加依赖:
<!--表示该服务是eureka client的客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--此以来是标准依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!--ribbon的依赖包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2.2 修改yml文件,在消费端连接eureka注册中心集群,不再简单的通过restTemplate直接转发访问服务提供者了,而是通过eureka通过负载均衡完成访问。
eureka:
client:
register-with-eureka: false #不向注册中心注册自己
fetch-registry: true #但是需要从注册中心读取查询服务
service-url:
defaultZone: http://eureka.8001.com:8001/eureka/, http://eureka.8001.com:8001/eureka/, http://eureka.8001.com:8001/eureka/
2.3 restTemplate创建Bean时添加注解@LoadBalanced,表示通过负载均衡的方式进行调用
2.4 启动类通过@EnableEurekaClient表明,该服务是一个客户端(消费者)
2.5 调用方式:由原来的通过直接明确的IP地址通过restTemplate的方式进行调用,修改为通过微服务的服务名进行调用的方式,从而达到消费端通过注册中心调用提供端
// public static final String URL_PRX = "http://localhost:7001";
public static final String URL_PRX = "http://producer-7001"; //通过服务名,来完成服务的调用
2.6 启动eureka集群,启动服务提供者,启动客户端,进行调用测试
3. ribbon的负载均衡调用
3.1 当提供者部署了集群之后,ribbon如果没有特别设置,会按照轮询的方式进行负载均衡依次调用。
3.2 提供者部署多个服务到注册中心之后,消费端会调用ribbon的负载均衡算法,进行轮询访问服务。算法调用机制如下:
4. ribbon负载均衡的服务调用机制(自带七种方式)
ribbon在没有设置的情况下,默认调用轮询方式进行调用服务,可以通过注册bean的方式进行配置ribbon调用服务所使用的机制,设置如下:
在服务消费端中:调用ribbon时声明机制即可
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
*ribbon负载均衡调用方式配置
* 默认是RoundRobinRule
* 需要什么方式就返回什么对象即可
* ribbon提供了七中调用方式(当前版本)
*/
@Bean
public IRule getIRule() {
return new RandomRule(); //设置ribbon的负载均衡机制为随机调用服务
}
5. ribbon的自定义负载均衡机制设置
5.1 在消费者端启动类中调用注解:@RibbonClient(name = "PRODUCER-DEPT", configuration = MyRule.class)
表示对于服务名称为PRODUCER-DEPT的微服务,负载均衡调用方式遵循MyRule类中规定的方式
注意:该注解使用在启动类中,并且该注解所定义的自动以机制类,不能放在ComponentScan所扫描的当前包以及子包下,否则我们自定义的配置类就会被所有的ribbon共享,就不能达到对某一个服务进行特殊配置的目的了。
5.2 myRule.java(简易版)
@Configuration
public class MyRule {
@Bean
public IRule getIRule() {
return new RoundRobinRule(); //设置ribbon的负载均衡机制为轮询调用服务
}
}
此处配置的IRule返回对象,不会与restTemplate公共ribbon设置的IRule所返回的类型对象所冲入,因为公共配置的(也就是能被@ComponentScan所扫描的)bean,是应用于全局的ribbon适用于所有的微服务,本处配置的bean,仅仅只针对RibbonClient注解所指定的服务,进行负载均衡配置
5.3 自定义ribbon负载均衡机制
我们可以自己写ribbon的负载均衡算法,只需要按照RandomRule类中的方式实现AbstractLoadBalancerRule这个抽象类中的方法,完成自己的算法逻辑,然后再启动类中指定服务的规则类,实例化时,返回自己定义的实体即可。
步骤如下:
5.3.1 创建MyStyleRule.java 根据git源码中的 RandomRule类进行修改的,逻辑为随即调用,每次调用五遍
/**
* ribbon负载均衡自定义规则
* 规则:随机调用,并且每次随机服务被重复调用5次
* 规则可以自定义算法逻辑进行设置
* @Date 2019/9/6 13:44
**/
public class MyStyleRule extends AbstractLoadBalancerRule {
private int index = 0;
private int times = 0;
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
/*int index = chooseRandomInt(serverCount);
server = upList.get(index);*/
if(times < 5) {
server = upList.get(index);
times++;
}else {
times = 0;
index = chooseRandomInt(serverCount);
server = upList.get(index);
times++;
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
5.3.2 在实例化IRule接口时,返回自己创建的规则类
@Bean
public IRule getIRule() {
/**
* 设置ribbon的负载均衡机制为轮询调用服务
*/
return new MyStyleRule();
}
此时,即可完成我们的自定义配置类