1.1spring cloud是什么
springcloud官网: https://spring.io/projects/spring-cloud#learn
spring cloud,基于springboot提供了一套微服务的解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件,除了基于NetFix的开源组件做高度封装之外,还有一些选型中立的开源组件,
springcloud利用了springboot的开发便利性,巧妙地简化了分布式系统的基础设施的开发,springcloud为开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,微代理,事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用springboot的开发风格做到一键部署和启动.
springcloud通过springboot的风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给我们留下了一套简单易懂,容易部署,容易维护的分布式系统开发工具包,
- springboot专注于快速开发单个个体微服务,
- springcloud是关注全局得微服务协调整理治理框架,他将springboot开发的一个个体微服务整合并管理起来,为各个服务之间提供配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,分布式会话等等集成服务,
- springboot可以离开springcloud独立使用,但是spirngcloud离不开springboot,属于依赖关系,
1.2springcloud和dubbo的技术选型
Dubbo和SpringCloud对比:
可以看一下社区活跃度
https://github.com/dubbo
https://github.com/springcloud
结果:
最大区别: springcloud抛弃了Dubbo的RPC通信,采用的是基于HTTP的REST方式
1.3springcloud能干什么
1.4总体介绍
2.1SpringCloud的版本选择
2.2创建父工程
这里就直接创建一个命名为SpringCloud-Rest的父工程即可
然后后面的项目都是该项目的子项目,都是maven空项目
建立一个数据库:db01
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chen</groupId>
<artifactId>springcloud-Rest</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>springcloud-consumer</module>
<module>spring-provider-8081</module>
<module>spring-cloud-Eureka</module>
<module>spring-cloud-Eureka-7002</module>
<module>spring-cloud-Eureka-7003</module>
<module>spring-cloud-provider-8082</module>
<module>spring-cloud-provider-8083</module>
<module>springcloud-consumer-80-feign</module>
<module>spring-cloud-provider-hystrix-8081</module>
<module>springcloud-consumer-hystrix-dashborad</module>
<module>spring-cloud-zuul</module>
<module>spring-cloud-config-server-3344</module>
<module>spring-cloud-config-client-3355</module>
<module>springcloud-config-eureka-7001</module>
<module>spring-cloud-provider-config-8001</module>
</modules>
<!--pom打包方式-->
<packaging>pom</packaging>
<properties>
<junit.version>4.12</junit.version>
<lombok.version>1.16.10</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<!--springcloud依赖包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springboot依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--日志门面-->
<dependency>
<groupId>ch.qos.loback</groupId>
<artifactId>loback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
dependencyManagement的使用简介
只是声明依赖,并不实现引入,因此子项目需要显示声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;另外如果子项目中指定了版本号,那么会使用子项目中指定的jar版本。
同理springbootdependency和springclouddependy管理了所有spring-cloud-xxx-xxx和spring-boot-xxx-xxx的版本,要注意的是springboot版本跟springcloud的版本关系
2.3建立一个子项目spring-cloud-api
这里的service包可忽略先,这是后面使用fegin要用到的.
pom.xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
Dept.java实体类:
package com.chen.springcloud.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true)//链式写法 new Dept().setDeptno().set.........
public class Dept implements Serializable {
private Long deptno;
private String deptname;
//这个数据是存在哪个数据库的,微服务,一个服务一个数据库,同一个数据可能保存在不同的数据库
private String db_source;
public Dept(String deptname) {
this.deptname = deptname;
}
}
2.4建立一个模块作为服务的提供者
建立一个模块命名为spring-cloud-provider-8081
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--监控信息依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--实体类写在springcloud-api中,这里就需要引入实体类的位置-->
<dependency>
<groupId>com.chen</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
</dependencies>
mapper
@Mapper
@Repository
public interface mapper {
//添加部门
public boolean addDept(Dept dept);
//查询一个部门
Dept queryOneDept(Long id);
//查询所有的部门
List<Dept> queryDept();
}
DeptServiceImpl
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private mapper deptDao;
public boolean addDept(Dept dept) {
return deptDao.addDept(dept);
}
public Dept queryOneDept(Long id) {
return deptDao.queryOneDept(id);
}
public List<Dept> queryDept() {
return deptDao.queryDept();
}
}
application.yml
server:
port: 8081
#mybatis的配置
mybatis:
mapper-locations: classpath:mapper/*.xml
#spring的配置
type-aliases-package: com.chen.springcloud.pojo
spring:
application:
name: spring-cloud-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: spring-provider-8001
DeptController
//提供Restfull风格
@RestController
public class DeptController {
@Autowired
private DeptServiceImpl deptService;
@PostMapping("/dept/add")
public boolean add(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("/dept/queryDeptById/get/{id}")
public Dept query(@PathVariable("id") Long id){
return deptService.queryOneDept(id);
}
@GetMapping("/dept/list")
public List list(){
return deptService.queryDept();
}
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.chen.mapper.mapper">
<insert id="addDept" parameterType="Dept">
insert into db01.dept(deptname, db_source) VALUES (#{dname},DATABASE())
</insert>
<select id="queryOneDept" resultType="Dept">
select * from db01.dept where deptno=#{id}
</select>
<select id="queryDept" resultType="Dept">
select * from db01.dept
</select>
</mapper>
主启动类
@SpringBootApplication
public class springApplication {
public static void main(String[] args) {
SpringApplication.run(springApplication.class,args);
}
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
//访问该页面就是监控页面
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
最后启动项目访问controller里面的接口即可,注意,实体类跟这个服务提供者不在同一个模块,我们照样可以拿到他,这就是微服务的简单拆分的小例子.
2.5建立一个模块作为服务消费者
建立一个模块命名为springcloud-consumer
MyRule包现在可忽略,这是自定义的一个算法包
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudLast</artifactId>
<groupId>com.bupt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-consumer-dept-80</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.bupt</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</project>
RestConfig
这里要用到 RestTemplate
,但是它的类中没有Bean,所以我们要把它注册到Bean中
@Configuration
public class RestConfig {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
Controller
@RestController
public class Controller {
@Autowired
RestTemplate restTemplate;
private static final String REST_URL_PREFIX="http://localhost:8081";
@RequestMapping("/dept/get/{id}")
public Dept getDept(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/queryDeptById/get/"+id,Dept.class);
}
@RequestMapping("/dept/add")
public Boolean addDept(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
@RequestMapping("/dept/list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
}
这里要说一下,我们刚刚写完服务提供者的时候,是通过http://localhost:8001/....的路径去访问的,这里就相当于与引用访问这个路径,所有我们先定义我们先访问的uri的前缀就是为http://localhost:8001
application.yml
server:
port: 80
主启动类
@SpringBootApplication
public class springApplication {
public static void main(String[] args) {
SpringApplication.run(springApplication.class,args);
}
}
最后启动服务提供者 spring-cloud-provider-8001
然后启动服务消费者 springcloud-consumer
通过服务消费者的url请求去获取服务提供者对应的请求,照样可以拿到
3.Eureka服务注册与发现
原理图
dubbo原理图
1. Eureka是什么
Eureka是一个基于REST的服务,主要用于AWS云中的定位服务,以实现中间层服务器的负载平衡和故障转移
在 Spring Cloud 微服务架构中通常用作注册中心
我们称这个服务为 Eureka Server,还有一个与之交互的客户端称之为 Eureka Client
服务在Eureka上注册,然后每隔30秒发送心跳来更新它们的租约。如果客户端不能多次续订租约,那么它将在大约90秒内从服务器注册表中剔除。注册信息和更新被复制到集群中的所有eureka节点。来自任何区域的客户端都可以查找注册表信息(每30秒发生一次)来定位它们的服务(可能在任何区域)并进行远程调用。
(PS:Eureka Client需要每30秒给Eureka Server发一次心跳,同时更新Server上最新的注册信息到本地,如果Server多次没有收到来自客户端的心跳,那么在90秒内会被Server上剔除
Eureka三大角色:
- Eureka Server 提供服务的注册与发现
- Eureka Provider 将自身的服务注册到Eureka中,便于消费者发现
- Eeureka Consumer 服务消费方从Eureka中获取注册服务列表,从而找到服务消费
springcloud-eureka-7001(Eureka服务注册中心)
pom.xml
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 7001
# Eureka配置
eureka:
instance:
# Eureka服务端的实例名字
hostname: eureka7001.com
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
fetch-registry: false
# Eureka监控页面~
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
主启动类
@SpringBootApplication
@EnableEurekaServer //EnableEurekaServer表示服务端的启动类,可以接收别人注册进来
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
启动之后访问 http://localhost:7001/
Eureka的自我保护机制
如果 Eureka 服务器检测到超过预期数量的注册客户端以一种不优雅的方式终止了连接,并且同时正在等待被驱逐,那么它们将进入自我保护模式。这样做是为了确保灾难性网络事件不会擦除eureka注册表数据,并将其向下传播到所有客户端。
任何客户端,如果连续3次心跳更新失败,那么它将被视为非正常终止,病句将被剔除。当超过当前注册实例15%的客户端都处于这种状态,那么自我保护将被开启。
当自我保护开启以后,eureka服务器将停止剔除所有实例,直到:
- 它看到的心跳续借的数量回到了预期的阈值之上,或者
- 自我保护被禁用
默认情况下,自我保护是启用的,并且,默认的阈值是要大于当前注册数量的15%
Eureka VS Zookeeper
Eureka保证AP
Eureka服务器节点之间是对等的,只要有一个节点在,就可以正常提供服务。
Eureka客户端的所有操作可能需要一段时间才能在Eureka服务器中反映出来,随后在其他Eureka客户端中反映出来。也就是说,客户端获取到的注册信息可能不是最新的,它并不保证强一致性
Zookeeper保证CP
Zookeeper集群中有一个Leader,多个Follower。Leader负责写,Follower负责读,ZK客户端连接到任何一个节点都是一样的,写操作完成以后要同步给所有Follower以后才会返回。如果Leader挂了,那么重新选出新的Leader,在此期间服务不可用。
为什么用Eureka
分布式系统大都可以归结为两个问题:数据一致性和防止单点故障。而作为注册中心的话,即使在一段时间内不一致,也不会有太大影响,所以在A和C之间选择A是比较适合该场景的。
把服务提供方注册到Eurea(spring-provider-8001)
1.导入eureka依赖
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
2.配置eureka
eureka:
client:
service-url:
#向哪里注册服务 注册中心的地址
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: spring-provider-8001
最后在主启动类上添加注解
@EnableEurekaClient //表示在服务启动后自动注册到Eureka中
启动springcloud-eureka-7001,启动完毕后再启动下面的服务
启动spring-provider-8001,等一会再次访问 http://localhost:7001/
actuator完善监控信息
所以我们肯定是少了什么东西,然后继续在服务提供方添加actuator监控的依赖
<!--actuator完善监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
然后再application配置文件配置监控信息
#eureka 的配置,服务注册到哪里
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka/
instance:
instance-id: springcloud-provider-dept8001 #修改Eureka上的默认的状态名字
#info配置(点击状态信息会返回的东西,可以百度)
info:
app.name: wulei-springcloud
company.name: blog.Tomstudy.com
Eureka集群搭建
修改域名映射
为了体验集群搭载在不同的电脑上,我们进入C:\Windows\System32\drivers\etc里面修改hosts文件,在文件的末尾添加下面几行
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
修改spring-cloud-eureka-7001的application.yml配置文件
server:
port: 7001
# Eureka配置
eureka:
instance:
# Eureka服务端的实例名字
hostname: eureka7001.com
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
fetch-registry: false
# Eureka监控页面~
service-url:
#重写Eureka的默认端口以及访问路径 --->http://localhost:7001/eureka/
# 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 集群(关联):7001关联7002、7003
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
建立第二个Eureka注册中心spring-cloud-eureka-7002
pom.xml
跟项目7001一模一样
主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
application.yml
server:
port: 7002
# Eureka配置
eureka:
instance:
# Eureka服务端的实例名字
hostname: eureka7002.com
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
fetch-registry: false
# Eureka监控页面~
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
建立第三个注册中心spring-cloud-eureka-7003
pom.xml
跟eureka-7001一模一样
主启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
application.yml
server:
port: 7003
# Eureka配置
eureka:
instance:
# Eureka服务端的实例名字
hostname: eureka7003.com
client:
# 表示是否向 Eureka 注册中心注册自己(这个模块本身是服务器,所以不需要)
register-with-eureka: false
# fetch-registry如果为false,则表示自己为注册中心,客户端的化为 ture
fetch-registry: false
# Eureka监控页面~
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://
然后启动7001、7002、7003项目
然后访问:http://localhost:7001/ 、http://localhost:7002/ 、http://localhost:7003/
8001项目注册多个注册中心
要把8001项目注册到多个注册中心上去,其实很简单,只需要改动配置文件即可
server:
port: 8081
#mybatis的配置
mybatis:
mapper-locations: classpath:mapper/*.xml
#spring的配置
type-aliases-package: com.chen.springcloud.pojo
spring:
application:
name: spring-cloud-provider
datasource:
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/db01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: spring-provider-8001
#监控所有
management:
endpoints:
web:
exposure:
include: "*"
然后启动8001项目,刷新http://localhost:7001/ 、http://localhost:7002/ 、http://localhost:7003/ 即可发现
4.Ribbon负载均衡
什么是Ribbon ?
Spring Cloud Ribbon是基于netfix Ribbon实现的一套客户端负载均衡的工具
简单来说,Ribbo是Netfix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netfix的中间层服务连接在一起,Ribbon的客户端软件提供了一系列完整的配置项如:连接超时,重试等等,简单来说,就是在配置文件中列出LoadBanlancer(简称LB : 负载均衡)后面的所有机器,Ribbon会自动的帮助你基于某种规则(或者说算法,如简单轮询,随机连接等等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法!
Ribbon能干什么?
- LB: 即负载均衡(Load Balance),在微服务或者分布式集群中经常使用的一种应用
- 负载均衡简单来说就是将用户的请求平摊的分配到多个服务上,从而达到系统的高可用.
- 常见的负载均衡软件有Nginx,lvs 等等
- dubbo,springcloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义
- 负载均衡简单分类:
集中式LB:
即在服务消费方和提供方之间建立独立的LB设施,如Nginx,由该设施负责把访问的请求通过某种策略发至服务方
进程式LB:
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器,
Ribbon就属于进程内的LB,他只是一个类库,集成消费方进程,消费方通过他获取到服务提供方的地址!
spring-consumer模块中使用ribbon
pom.xml
<dependencies>
<!--实体类-->
<dependency>
<groupId>com.chen</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--要去注册中心拿到服务还需要引入eureka的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
由于消费方客户端是利用RestTemplate来进行服务的读取,所以我们让RestTemplate实现负载均衡,只需要添加一个注解即可:@LoadBalance
RestConfig.java
@Configuration
public class RestConfig {
@Bean
@LoadBalanced //实现负载均衡 复杂均衡是作用在客户端的
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
我们这里导入了eureka的依赖,所以还要配置eureka
application.yml
server:
port: 80
#配置注册中心的地址 去拿到服务
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
主启动类
@SpringBootApplication
@EnableEurekaClient //开启eureka,消费方客户端 //在微服务启动的时候就能加载我们自己自定义配置的得Ribbon类
@RibbonClient(name="SPRING-CLOUD-PROVIDER",configuration = ChenIRule.class) //这里是因为我们自己配置的IRule不跟启动类同一个包下,这里就需要配置,
public class springApplication {
public static void main(String[] args) {
SpringApplication.run(springApplication.class,args);
}
}
最后还有一个问题,我们这里的@RibbonClient的name属性是通过服务的名字去获取服务的,我们的RestTemplate已经通过@LoadBalance实现了负载均衡,还是不够的,我们还要改变RestTemplae的请求路径,让其自动选择name为SPRING-CLOUD-PROVIDER的服务,而不是直接写死为http://localhost:8081
Controller
@RestController
public class Controller {
@Autowired
RestTemplate restTemplate;
//要实现负载均衡这里就不能写死 private static final String REST_URL_PREFIX="http://localhost:8081";
//要根据服务的id(也就是各个服务application.yml配置文件配置的spring.application.name的值)去查找服务
private static final String REST_URL_PREFIX="http://spring-cloud-provider";
@RequestMapping("/dept/get/{id}")
public Dept getDept(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/queryDeptById/get/"+id,Dept.class);
}
@RequestMapping("/dept/add")
public Boolean addDept(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
}
@RequestMapping("/dept/list")
public List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
}
最后启动7001、7002、7003项目后再启动8001项目,等8081项目注册到它们3个中后启动80项目
然后访问 http://localhost/dept/list 可以看到正常返回结果,当然了,在这里也看不出负载均衡,所以下面会配置多个服务提供者和多个数据库,来测试负载均衡的效果。
Ribbon实现负载均衡原理图
另外新建两个数据库db02,db03
创建另外两个服务提供者:8002、8003
直接新建两个子model,然后把8001的所有文件全部拷贝(提供相同的服务),一摸一样的,然后更改一下配置文件即可
pom.xml依赖
application.yml的端口号,对应的数据库,还有instance-id,例如:instance-id: springcloud-provider-dept8002
注意:下面的这个服务ID不要改
spring:
application:
name: springcloud-provider-dept # 3个服务名称一致是前提
现在的项目预览
然后,启动
springcloud-config-eureka-7001
springcloud-config-eureka-7002
springcloud-config-eureka-7003
springcloud-provider-dept-8001
springcloud-provider-dept-8002
springcloud-provider-dept-8003
springcloud-consumer
然后访问http://localhost/dept/list ,多访问几次,查询的数据没变,但是数据库变了
你会发现是轮询,就是每个服务轮流来,这也Ribbon的默认算法
Fegin负载均衡
简介:fegin旨在使得编写java Http客户端变得更加容易
- fegin是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似Controller调用Service. SpringCloud集成了Ribbon和Eurea,可在使用Fegin时提供负载均衡的http客户端.
- 只需要创建一个接口,然后添加注解即可
- fegin 主要是社区,大家都习惯面向接口编程,这个是很多人的规范,调用微服务访问的两种方式
- 微服务名字{Ribbon}
- 接口和注解{Fegin}
Fegin能干什么
在Fegin的实现下,我们只需要创建一个接口并使用注解的方式来配置它,类似以前的Dao接口标注Mapper注解,现在是一个微服务接口上标注一个Fegin的注解即可,即可完成对服务提供方的接口绑定,简化了使用SpringCloud Ribbon时,自动封装服务调用客户端的开发量
Fegin集成了Ribbon
新建一个和springcloud-consumer一模一样的项目,springcloud-consumer-80-fegin,这里主要是为了区分这里是使用fegin实现的负载均衡.
pom.xml
<dependencies>
<!--实体类-->
<dependency>
<groupId>com.chen</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--要去注册中心拿到服务还需要引入eureka的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.5.RELEASE</version>
</dependency>
</dependencies>
修改springcloud-api模块
添加一个接口
DeptClientService
@FeignClient(value ="SPRING-CLOUD-PROVIDER")//配置服务的地址也就是服务的id
@Component
public interface DeptClientService {
/*
注意:这里的mapping要跟服务的提供者的controller的mapping映射路劲要完全一致,否则会报错
*/
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
@GetMapping("/dept/queryDeptById/get/{id}")
Dept queryOneDept(@PathVariable("id") Long id);
@GetMapping("/dept/list")
List<Dept> queryDept();
}
在客户端主启动类添加
@EnableFeignClients(basePackages = {"com.chen.springcloud"})
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.chen.springcloud"})
public class springApplication80 {
public static void main(String[] args) {
SpringApplication.run(springApplication80.class,args);
}
}
最后启动7001、… 、8001、… 、feign的80项目,测试!