SpringCloud,基于SpringBoot提供的一套微服务解决方案,包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,等组件。换句话说是分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微服务全家桶。
首先可以在GitHub上看到二者的活跃度,其次是比较各功能组件的支持情况,最大的区别在于SpringCloud抛弃了Dubbo的RPC通信,采用的是HTTP的REST方式,如下:
SpringCloud | Dobbo |
---|---|
服务注册中心 | Zookeeper |
服务调用方式 | RPC |
服务监控 | Dubbo-monitor |
断路器 | 不完善 |
服务网关 | 无 |
分布式配置 | 无 |
服务跟踪 | 无 |
消息总线 | 无 |
数据流 | 无 |
批量任务 | 无 |
SpringCloud各个组件的文档:https://springcloud.cc/spring-cloud-netflix.html
SpringCloud中文API:https://springcloud.cc/spring-cloud-dalston.html
SpringCloud版本:Dalston.SR1,SpringBoot版本:1.5.9
项目是使用SpringCloud将四个工程进行整合,microservicecloud整体父工程Project,microservicecloud-api公共子模块Module,microservicecloud-provider-dept-8001部门微服务提供者Module,microservicecloud-consumer-dept-80部门微服务消费者Module。
在逻辑视图中选择new-Maven Project-勾上创建简单项目-选择pom方式
<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.0modelVersion>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>pompackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Dalston.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>1.5.9.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.0.4version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.0.31version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>1.3.0version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
<scope>testscope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
dependencies>
dependencyManagement>
project>
在父项目上创建microservicecloud-api项目,注意是在microservicecloud上new一个maven module,packaging选择jar
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-apiartifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
dependencies>
project>
public class Dept implements Serializable{
private Long deptno; // 主键
private String dname; // 部门名称、
private String db_source; // 来自那个数据库,因为微服务可以一个服务对应一个数据库,同一个信息被存储到不同的数据库
public Dept() {
super();
}
public Dept(Long deptno, String dname, String db_source) {
super();
this.deptno = deptno;
this.dname = dname;
this.db_source = db_source;
}
public Long getDeptno() {
return deptno;
}
public void setDeptno(Long deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getDb_source() {
return db_source;
}
public void setDb_source(String db_source) {
this.db_source = db_source;
}
@Override
public String toString() {
return "Dept [deptno=" + deptno + ", dname=" + dname + ", db_source=" + db_source + "]";
}
当我们每次都需要创建一个实体类的getter,setter,toString和构造器等方法时,如果增加一个字段就要重新生成方法,为了简化这种重复的操作,我们在前面的pom中引入了lombok,同样的实体类,使用方法如下
@SuppressWarnings("serial")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain=true)
public class Dept implements Serializable{
private Long deptno; // 主键
private String dname; // 部门名称
// 来自那个数据库,因为微服务可以一个服务对应一个数据库,同一个信息被存储到不同的数据库
private String db_source;
}
拷贝lombok-1.16.18.jar到Eclipse目录下,执行java -jar D:\javasoft\eclipse-jee-neon-3-win32-x86_64\eclipse\ombok-1.16.18.jar,然后,弹框中选择Eclipse安装目录,选择install即可。
@Data :注解在类上;提供类所有属性的 getting 和 setting 方法,此外还提供了equals、canEqual
@Setter:注解在属性上;为属性提供 setting 方法
@Getter:注解在属性上;为属性提供 getting 方法
@Log4j :注解在类上;为类提供一个 属性名为log 的 log4j 日志对象
@NoArgsConstructor:注解在类上;为类提供一个无参的构造方法
@AllArgsConstructor:注解在类上;为类提供一个全参的构造方法
@Accessors(chain=true):可以使用链式写法
@SuppressWarnings("serial")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Accessors(chain=true)
public class Dept implements Serializable{
private Long deptno; // 主键
private String dname; // 部门名称、
private String db_source; // 来自那个数据库,因为微服务可以一个服务对应一个数据库,同一个信息被存储到不同的数据库
public static void main(String[] args) {
Dept dept = new Dept();
dept.setDeptno(12L).setDname("开发部").setDb_source("DB01");
}
}
实体类必须实现Serializable接口
公共组件模块写好后可以点击run as 选择maven clean ,然后在选择maven install。其他模块引用的方法如下
<groupId>com.atguigu.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
首先现在父类项目上new一个maven module,microservicecloud-provider-dept-8001修改pom.xml文件
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-provider-dept-8001artifactId>
<dependencies>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.luo.springcloud.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 1234
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
<configuration>
<settings>
<setting name="cacheEnabled" value="true" />
settings>
configuration>
DROP DATABASE IF EXISTS cloudDB01 ;
CREATE DATABASE cloudDB01 CHARACTER SET UTF8 ;
USE cloudDB01 ;
CREATE TABLE dept (
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR (60),
db_source VARCHAR (60)
) ;
INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());
@Mapper
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept findById(Long id);
public List findAll();
}
<mapper namespace="com.luo.springcloud.dao.DeptDao">
<select id="findById" resultType="Dept" parameterType="Long">
select deptno,dname,db_source from dept where deptno=#{deptno};
select>
<select id="findAll" resultType="Dept">
select deptno,dname,db_source from dept;
select>
<insert id="addDept" parameterType="Dept">
INSERT INTO dept(dname,db_source) VALUES(#{dname},DATABASE());
insert>
mapper>
public interface DeptService {
public boolean add(Dept dept);
public Dept get(Long id);
public List list();
}
@Service
public class DeptServiceImpl implements DeptService{
@Autowired
private DeptDao dao;
@Override
public boolean add(Dept dept) {
return dao.addDept(dept);
}
@Override
public Dept get(Long id) {
return dao.findById(id);
}
@Override
public List list() {
return dao.findAll();
}
}
@RestController
public class DeptController {
@Autowired
private DeptService service;
@RequestMapping(value="/dept/add",method=RequestMethod.POST)
public boolean add(@RequestBody Dept dept){
return service.add(dept);
}
@RequestMapping(value="dept/get/{id}",method=RequestMethod.GET)
public Dept get(@PathVariable("id") Long id){
return service.get(id);
}
@RequestMapping(value="dept/list",method=RequestMethod.GET)
public List list(){
return service.list();
}
}
@SpringBootApplication
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class, args);
}
}
输入http://localhost:8001/dept/list以JSON的方式返回数据
首先现在父类项目上new一个maven module,microservicecloud-consumer-dept-80修改pom.xml文件
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-consumer-dept-80artifactId>
<description>部门微服务消费者description>
<dependencies>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
server:
port: 80
@Configuration
public class ConfigBean {
@Bean
public RestTemplate geRestTemplate(){
return new RestTemplate();
}
}
RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单高效便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具类集,使用方法如下
(url,requestMap,ResponseBean.class)三个参数分别代表Rest请求地址,请求参数,HTTP响应转换被转换的对象类型
@RestController
public class DeptController_Consumer {
private static final String REST_URL_PREFIX = "http://localhost:8001";
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/consumer/dept/add")
public boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, Boolean.class);
}
@RequestMapping(value="/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
}
@RequestMapping(value="/consumer/dept/list")
public Dept list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list/", Dept.class);
}
}
@SpringBootApplication
public class DeptConsumer80_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
http://localhost/consumer/dept/list
http://localhost/consumer/dept/get/2
http://localhost/consumer/dept/add?dname=AI
在上述项目的父工程中新建microservicecloud-eureka-7001,这个module是Eureka的服务中心
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-eureka-7001artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer// EurekaServer服务器端启动类,接收其它微服务注册进来
public class EurekaServer7001_App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001_App.class, args);
}
}
浏览器输入http://localhost:7001/,看到Spring Eureka界面表示成功,这个访问链接和程序中的application.yml配置吻合。
将microservicecloud-provider-dept-8001微服务注册到microservicecloud-eureka-7001中
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka
说明:defaultZone的地址对应Eureka Server服务注册中心的application.yml中的defaultZone路径
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会注册到Eureka服务注册中心
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class, args);
}
}
先启动Eureka服务注册中心microservicecloud-eureka-7001,启动微服务microservicecloud-provider-dept-8001,打开浏览器输入http://localhost:7001/,Application下出现MICROSERVICECLOUD-DEPT微服务名称,这个名称来源于microservicecloud-provider-dept-8001中application.ym文件中的配置属性,如下
spring:
application:
name: microservicecloud-dept
在Eureka中注册的微服务的Status的名称显示localhost或者显示电脑主机名,所以要修改服务的主机名称,修改方法如下,修改microservicecloud-provider-dept-8001中application.yml文件,修改后如下
instance:
instance-id: microservicecloud-dept8001
修改microservicecloud-provider-dept-8001中application.yml文件,修改后如下
instance:
instance-id: microservicecloud-dept8001
prefer-ip-address: true #访问路径可以显示IP地址
增加microservicecloud-provider-dept-8001中POM.xml文件
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
总的父工程microservicecloud修改pom.xml添加构建build信息
<build>
<finalName>microservicecloudfinalName>
<resources>
<resource>
<directory>src/main/resourcesdirectory>
<filtering>truefiltering>
resource>
resources>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-resources-pluginartifactId>
<configuration>
<delimiters>
<delimit>$delimit>
delimiters>
configuration>
plugin>
plugins>
build>
修改microservicecloud-provider-dept-8001中application.yml文件,修改后如下
info:
app.name: luokangyuan-microservicecloud
company.name: www.luokangyuan.com
build.artifactId: $project.artifactId$
build.version: $project.version$
默认情况下,如果EurekaServer在一定的时间内没有接收到某一个微服务实例的心跳,EurekaServer将会注销该实例,页面就会看见一串红色提示,但是当网络分区发生故障时,微服务与EurekaServer无法进行正常的通信,此时本不应该注销这个微服务实例,这个时候,Eureka的自我保护机制就可以解决这个问题,当EurekaServer节点在短时间内丢失过多的客户端时(可能发生了网络故障),那么这个节点就会进入自我保护模式,一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务),当网络故障恢复后,该EurekaServer节点就会自动退出自我保护模式。
在自我保护模式下,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例,当它收到的心跳数重新到阈值以上,该EurekaServer就会自动退出自我保护模式,也就是宁可保留错误的服务注册信息,也不盲目的删除任何可能健康的服务实例。
对于注册近Eureka里面的微服务,可以通过服务发现来获取该服务的信息
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
public Object discovery(){
List list = client.getServices();//得到Eureka中所有的微服务
System.out.println("**********" + list);
List srvList = client.getInstances("MICROSERVICECLOUD-DEPT");
for (ServiceInstance element : srvList) {
System.out.println(element.getServiceId() + "\t"
+ element.getHost() + "\t" + element.getPort() + "\t"
+ element.getUri());
}
return this.client;
}
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会注册到Eureka服务注册中心
@EnableDiscoveryClient // 服务发现
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class, args);
}
}
启动服务注册中心microservicecloud-eureka-7001,再启动microservicecloud-provider-dept-8001,访问http://localhost:8001/dept/discovery可以得到这个服务的info信息,/dept/discovery接口就是microservicecloud-provider-dept-8001这个服务暴露给外部访问的接口。使用http://localhost:8001/dept/discovery测试,就是自己测试能不能使用
microservicecloud-consumer-dept-80调用microservicecloud-provider-dept-8001服务暴露在外的接口,修改microservicecloud-consumer-dept-80中的DeptController_Consumer,如下
// 测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = "/consumer/dept/discovery")
public Object discovery(){
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
}
启动microservicecloud-consumer-dept-80访问http://localhost/consumer/dept/discovery得到8001微服务信息
microservicecloud-eureka-7001使EurekaServer服务注册中心,一旦这个出现问题,那么微服务就不能正常的工作,为防止这种情况,所以出现了集群,就是建立多个microservicecloud-eureka-7002,microservicecloud-eureka-7003等服务注册中心。
在7001注册中的application.yml文件中hostname,不能与7002,7003相同,所以要做映射配置
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
修改C:\Windows\System32\drivers\etc\host文件,让127.0.0.1有三个别名
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
server:
port: 7003
eureka:
instance:
hostname: eureka7003.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
dept微服务会同时注册到7001,7002,7003服务注册中心
eureka:
client: #客户端注册进eureka服务列表内
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
Eureka遵循AP原则,Zookeeper遵循CP原则,C:强一致性,A:可用性,P:分区容错性
著名的CAP理论中提出,一个分布式系统不可能同时满足C(一致性)A(可用性)P(分区容错性),由于分区容错性p是分布式系统中必须保证,因此只能在A和C之间权衡
在Zookeeper中存在一种情况下,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举,但是,选举leader的时间太长,且选举过程中这个Zookeeper集群是不可用的,这就导致在选举期间注册服务瘫痪,在云部署的环境中,因为网络问题使得Zookeeper集群失去master节点的可能性较大,虽然服务最终能够恢复,但是在漫长的选举时间导致的注册时间不可用是不能容忍的,当我们向注册中心查询注册列表时,可以忍受注册中心返回的是几分钟以前的注册信息,但是不能接收服务直接down不可用,也就是说,服务注册对可用性的要求高于一致性。
Eureka知道Zookeeper的不足,所以设计最初就保证可用性,Eureka各个节点都是平等的,几个节点的挂点不会影响其他正常节点的工作,剩余的节点仍然可以提供注册和查询服务,只不过不能保证查询的信息是最新的,除此之外,Eureka还有一种自我保护机制,当过多的节点没有正常的心跳时,那么Eureka就会认为客户端出现了网络故障,此时Eureka会
Spring Cloude Ribbon是基于Netfilx Ribbon实现的一套客户端 负载均衡的工具,简单说,Ribbon是Netfilix发布的开源项目,主要功能就是提供 客户端的软件负载均衡算法,将Netfilix的中间层服务连接在一起,Ribbon客户端组件提供了一系列完善的配置项如连接超时,重试等,简单说,就是在配置文件中列出Load Balance后面的所有机器,Ribbon会自动的帮助你基于某种算法规则(简单轮询,随机连接等)去连接这些机器,也可以使用Ribbon自定义负载均衡算法。LB,即负载均衡,在微服务或者分布式集群中常用的一种应用。负载均衡就是将用户的请求平摊的分配到多个服务上,从而达到HA,常见的负载均衡软件有Nginx,LVS,硬件F5等
由于Ribbon是客户端的负载均衡工具,所以我们需要修改的是客户端项目microservicecloud-consumer-dept-80
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
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/
由于客户端使用restTemplate访问服务端中的数据接口,restTemplate配置在服务端的配置类中,所以修改如下
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced
public RestTemplate geRestTemplate(){
return new RestTemplate();
}
}
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer80_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT";
启动7001,7002,7003三个服务注册中心,启动8001服务提供者,启动80客户端,使用http://localhost/consumer/dept/list可以渠道对应的数据,在DeptController_Consumer使用的是http://MICROSERVICECLOUD-DEPT服务名称来调用服务的接口,相比之前的http://localhost:8001,Ribbon和Eureka整合后,Consumer可以直接通过服务名称来调用服务,而不再关心地址和端口号。
目前只有一个microservicecloud-provider-dept-8001服务提供者,为了实现Ribbon的负载均衡,所以我们需要多个服务提供者实例,新建microservicecloud-provider-dept-8002,microservicecloud-provider-dept-8003两个Module。参考8001的pom.xml文件修改8002,8003的pom.xml文件。拷贝8001中的所以类和配置文件mybatis和application.yml文件,将主启动类修改为对应的名字
DROP DATABASE IF EXISTS cloudDB02 ;
CREATE DATABASE cloudDB02 CHARACTER SET UTF8 ;
USE cloudDB02 ;
CREATE TABLE dept (
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR (60),
db_source VARCHAR (60)
) ;
INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());
server:
port: 8002
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.luo.springcloud.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB02 # 数据库名称
username: root
password: 1234
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200
DROP DATABASE IF EXISTS cloudDB03 ;
CREATE DATABASE cloudDB03 CHARACTER SET UTF8 ;
USE cloudDB03 ;
CREATE TABLE dept (
deptno BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
dname VARCHAR (60),
db_source VARCHAR (60)
) ;
INSERT INTO dept(dname,db_source) VALUES('开发部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('人事部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('财务部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('市场部',DATABASE());
INSERT INTO dept(dname,db_source) VALUES('运维部',DATABASE());
server:
port: 8003
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.luo.springcloud.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB03 # 数据库名称
username: root
password: 1234
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200
三个微服务提供者连接不同的数据库,因此在application.yml文件中,我们需要修改端口号和连接的数据库,注意的是三个微服务提供者的微服务名字保持一样,也就是如下的配置信息
spring:
application:
name: microservicecloud-dept
访问连接http://localhost:8001/dept/list,http://localhost:8002/dept/list,http://localhost:8003/dept/list得到不同数据库数据,当我们启动服务注册中心7001,7002,7003,再启动80客户端,这个时候访问localhost/consumer/dept/list,每次刷新就会得到不同数据库的数据。这就是Ribbon默认的轮询算法的负载均衡。
Ribbon默认提供的是轮询的负载均衡算法,完整了还有如下
RoundRobinRule | 轮询 |
---|---|
RandomRule | 随机 |
AvaliabilityFilteringRule | 会先过滤由于多次访问故障而处于断路器跳闸的状态的服务和并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略 |
WeightedResponseTimeRule | 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大 |
RetryRule | 先按照RoundRobinRule策略获取服务,如果获取服务失败会在指定时间内重试 |
BestAvailableRule | 会先过滤掉由于多次访问故障二处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 |
ZoneAvoidanceRule | 默认规则,复合判断server所在的区域的性能和server的可用性选择服务器 |
在客户端的配置类ConfigBean.java中添加IRule的实现
@Configuration
public class ConfigBean {
@Bean
@LoadBalanced
public RestTemplate geRestTemplate(){
return new RestTemplate();
}
@Bean
public IRule myRule(){
return new RandomRule();
}
}
如果不使用Ribbon默认的七种负载均衡算法,这个时候就需要使用自定义负载均衡算法
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
特此说明
RibbonClient注解中的MySelfRule类使我们自定义负载均衡算法的类,但是,这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们这个自定义的配置类会被所有的Ribbon客户端所共享,也就说,达不到我们特殊化定制的目的。举例说明,自定义配置类不能放在项目主启动类所有的包以及子包下,因为主启动类使用注解@SpringBootApplication,这个注解点进去使用@ComponentScan注解
轮询算法中每一个服务轮询一次,现在需求是每一个服务调用五次后在轮询下一个服务
package com.luo.myrule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule_lky();
}
}
package com.luo.myrule;
import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class RandomRule_lky extends AbstractLoadBalancerRule{
// total = 0 // 当total==5以后,我们指针才能往下走,
// index = 0 // 当前对外提供服务的服务器地址,
// total需要重新置为零,但是已经达到过一个5次,我们的index = 1
// 分析:我们5次,但是微服务只有8001 8002 8003 三台,OK?
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
private int currentIndex = 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 upList = lb.getReachableServers();
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// private int total = 0; // 总共被调用的次数,目前要求每台被调用5次
// private int currentIndex = 0; // 当前提供服务的机器号
if(total < 5)
{
server = upList.get(currentIndex);
total++;
}else {
total = 0;
currentIndex++;
if(currentIndex >= upList.size())
{
currentIndex = 0;
}
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key){
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig){}
}
Feign是一个声明式WebService客户端,使用Feign能够让编写Web Service客户端变得更简单,它的使用方法就是定义一个接口,然后在上面添加注解。SpringCloud对Feign进行了封装,支持SpringMVC注解和HTTPMessageConverters,Feign可以与Eureka和Ribbon组合使用以支持负载均衡。简单讲,只需要创建一个接口,然后在上面使用注解即可。
参考项目microservicecloud-consumer-dept-80新建microservicecloud-consumer-dept-feign,拷贝相应的包和配置文件,去掉IRule等信息,修改pom.xml文件,添加对Feign的支持
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-consumer-dept-feignartifactId>
<dependencies>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
由于Feign是面向接口编程,为方便接口的互相调用,将接口和公共的方向在项目microservicecloud-api中,因此修改为:
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list",method = RequestMethod.GET)
public List list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}
@RestController
public class DeptController_Consumer {
@Autowired
private DeptClientService service;
@RequestMapping(value = "/consumer/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
return this.service.get(id);
}
@RequestMapping(value = "/consumer/dept/list")
public List list(){
return this.service.list();
}
@RequestMapping(value = "/consumer/dept/add")
public Object add(Dept dept){
return this.add(dept);
}
}
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.luo.springcloud"})
@ComponentScan("com.luo.springcloud")
public class DeptConsumer80_Feign_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_Feign_App.class, args);
}
}
启动3个Eureka集群,启动三个部门微服务提供者,启动Feign,访问http://localhost/consumer/dept/list即可
Feign集成了Ribbon,利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并通过轮询的方式实现了客户端的复杂均衡,与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式法人方法,优雅而简单的实现服务调用。
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多的依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下, 不会导致整体服务的失败,避免级联故障,以提高分布式系统的弹性。断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝), 向调用方法返回一个预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法异常无法处理的异常,这样就保证服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延。
熔断机制是应对雪崩效应的一种微服务链路保护机制,当扇出链路的某一个微服务不可用或者响应时间太长,会进行服务的降级, 进而熔断该节点微服务的调用,快速返回“错误”的响应信息,当检测到该节点微服务调用响应正常后恢复调用链路,在SpringCloud框架中熔断机制使用Hystrix实现,Hystrix会监控微服务调用情况,当失败达到一定阈值。就会启动熔断机制,熔断机制的注解是 @HystrixCommand
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-provider-dept-hystrix-8001artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
eureka:
client: #客户端注册进eureka服务列表内
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001-hystrix #自定义服务名称信息
prefer-ip-address: true #访问路径可以显示IP地址
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.luo.springcloud.entities # 所有Entity别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper映射文件
spring:
application:
name: microservicecloud-dept
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动包
url: jdbc:mysql://localhost:3306/cloudDB01 # 数据库名称
username: root
password: 1234
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: #客户端注册进eureka服务列表内
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: microservicecloud-dept8001-hystrix #自定义服务名称信息
prefer-ip-address: true #访问路径可以显示IP地址
info:
app.name: luokangyuan-microservicecloud
company.name: www.luokangyuan.com
build.artifactId: $project.artifactId$
build.version: $project.version$
Hystrix的作用就是当调用服务出现异常时如何解决,模拟根据id查部门信息,查到null,人为抛出运行时异常,让Hystrix处理这种情况。
@RequestMapping(value="dept/get/{id}",method=RequestMethod.GET)
@HystrixCommand(fallbackMethod = "processHystrix_GET")
public Dept get(@PathVariable("id") Long id){
Dept dept = service.get(id);
if(null == dept){
throw new RuntimeException("该ID:"+id+"没有对应的部门信息");
}
return dept;
}
public Dept processHystrix_GET(@PathVariable("id") Long id){
return new Dept().setDeptno(id)
.setDname("该ID:"+id+"没有对应的信息,null--@HystrixCommand")
.setDb_source("no this database in Mysql");
}
@SpringBootApplication
@EnableEurekaClient // 本服务启动后会注册到Eureka服务注册中心
@EnableDiscoveryClient // 服务发现
@EnableCircuitBreaker //对Hystrix熔断机制的支持
public class DeptProvider8001_Hystrix_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
}
}
启动三个Eureka集群,启动服务主启动类DeptProvider8001_Hystrix_App,客户端启动microservicecloud-consumer-dept-80,页面访问http://localhost/consumer/dept/get/112
服务降级处理是在客户端完成的,与服务端没有关系,在前面的服务熔断中,我们发现每一个业务方法都要写一个processHystrix_方法,这样就造成了很大耦合,根据Spring的学习,我们可将processHystrix_改写一个异常通知。
根据已有的DeptClientService接口,新建一个实现了FallbackFactory接口的类DeptClientServiceFallbackFactory
package com.luo.springcloud.service;
import java.util.List;
import org.springframework.stereotype.Component;
import com.luo.springcloud.entities.Dept;
import feign.hystrix.FallbackFactory;
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService>{
@Override
public DeptClientService create(Throwable arg0) {
return new DeptClientService() {
@Override
public List list() {
return null;
}
@Override
public Dept get(long id) {
return new Dept().setDeptno(id)
.setDname("该ID:"+id+"没有对应的信息,Consumer客户端提供的降级信息,此服务暂停使用")
.setDb_source("no this database in Mysql");
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
注意:不要忘记新类上添加@Component注解
在DeptClientService接口在注解@FeignClient(value = “MICROSERVICECLOUD-DEPT”)添加fallbackFactory属性值
@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
public Dept get(@PathVariable("id") long id);
@RequestMapping(value = "/dept/list",method = RequestMethod.GET)
public List list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}
server:
port: 80
feign:
hystrix:
enabled: true
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
启动三个Eureka集群,microservicecloud-provider-dept-8001启动,microservicecloud-consumer-dept-feign启动,正常访问http://localhost/consumer/dept/get/1测试,故意关停microservicecloud-provider-dept-8001,客户端自己调用提示
Hystrix还提供了准实时的调用监控Hystrix Dashboard,Hystx会持续的记录所有通过Hystrix发起的请求的执行信息,并以统计报表的图形的形式展示给用户,包括每秒执行多少次请求多少成功多少失败等,对监控内容转换为可视化界面。
新建microservicecloud-consumer-hystrix-dashboard监控的一个微服务工程
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-consumer-hystrix-dashboardartifactId>
<dependencies>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-ribbonartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-feignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrix-dashboardartifactId>
dependency>
dependencies>
project>
server:
port: 9001
@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumer_DashBoard_App {
public static void main(String[] args) {
SpringApplication.run(DeptConsumer_DashBoard_App.class, args);
}
}
所有的Provider微服务提供类(8001,8002,8003)都需要监控依赖配置,也就是pom文件添加如下依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
启动microservicecloud-consumer-hystrix-dashboard,访问http://localhost:9001/hystrix,出现豪猪页面
启动3个Eureka集群,启动microservicecloud-provider-dept-hystrix-8001,启动了microservicecloud-consumer-hystrix-dashboard用来监控8001服务提供者,访问http://localhost:8001/hystrix.stream
访问http://localhost:9001/hystrix,填写监控地址http://localhost:8001/hystrix.stream,时间2000,title:demo01,点击按钮
实心圆:两种含义,它通过颜色的变化代表了实例的健康程度,健康色是从绿色<黄色<橙色<红色递减,该实心圆除了颜色的变化之外,他的大小也会根据实例的请求流量发生变化,流量越大该实心圆就越大,所以通过实心圆的展示就可以在大量实例中快速的发现 故障实例和高压力测试。
Zuul包含了对请求的路由和过滤两个主要的功能,其中路由的功能是负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础而过滤功能是负责对请求的处理过程进行干预,是实现请求校验,服务聚合等功能的基础,Zuul和Eureka进行整合,将Zuul自身注册近Eureka服务治理的应用,同时从Eureka中获取其他微服务的消息,也及时以后的访问服务都是通过Zuul跳转后获得, 注意的是Zuul服务最终还是会注册近Eureka中
新建项目microservicecloud-zuul-gateway-9527,添加依赖如下
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-zuul-gateway-9527artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloud-apiartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
server:
port: 9527
spring:
application:
name: microservicecloud-zuul-gateway
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
instance:
instance-id: gateway-9527.com
prefer-ip-address: true
zuul:
#ignored-services: microservicecloud-dept
prefix: /luo
ignored-services: "*"
info:
app.name: luo-microcloud
company.name: www.luo.com
build.artifactId: $project.artifactId$
build.version: $project.version$
127.0.0.1 myzuul.com
@SpringBootApplication
@EnableZuulProxy
public class Zuul_9527_StartSpringCloudApp {
public static void main(String[] args) {
SpringApplication.run(Zuul_9527_StartSpringCloudApp.class, args);
}
}
三个集群,一个服务提供类microservicecloud-provider-dept-8001,一个路由
不使用路由:http://localhosat:8001/dept/get/2
使用路由:http://myzuul.com:9527/microservicecloud-dept/dept/get/2
在前面的测试中我们可以使用http://myzuul.com:9527/microservicecloud-dept/dept/get/2访问我们的接口,这样就暴露我们的微服务名称,需要做安全加固,就用到了路由访问映射,修改路由项目的yml文件,添加 mydept.path: /mydept/**
zuul:
#ignored-services: microservicecloud-dept #忽略真实地址,只让虚拟地址访问
prefix: /luo #访问地址前缀
ignored-services: "*"#忽略真实地址,只让虚拟地址访问
routes:
mydept.serviceId: microservicecloud-dept ##真实地址
mydept.path: /mydept/** # 虚拟地址
访问连接:http://lyzuul.com:9527/luo/mydept/dept/get/1
就前面项目而言,分布面临的问题是配置问题,每一个项目都有一个yml文件,不好运维管理,所有需要一套集中式,动态的配置管理设施,SpringCloud提供了ConfigServer来解决这个问题。
SpringCloud Config是为微服务架构中的微服务提供集中化的外部配置支持,配置服务器为 各个不同的微服务应用的环境提供了一个 中心化的外部配置。SpringCloud Config分为客户端和服务端,服务端也称 分布式配置中心,它是一个独立的微服务应用,用来连接配置服务器并为客户端提供获取配置信息,加密和解密信息等访问接口,客户端是通过指定的配置中心获取和加载配置信息配置服务器默认采用git来存储配置信息,这样就有助于对环境配置进行版本管理,并且可以通过git客户端工具管理和访问配置内容。
GitHUb上新建一个microservicecloud-config的Repository
本地硬盘目录新建git仓库并clone
在D:\workspace2018\micorservicecloude-config\microservicecloud-config新建application.yml文件
Spring:
profiles:
active:
- dev
---
Spring:
profiles: dev
application:
name: micorservicecloud-config-luo-dev
---
Spring:
profiles: test
application:
name: micorservicecloud-config-luo-test
注意保存为utf-8的文件格式
将yml文件推送到GitHub上
git add .
git commit -m""
git push origin master
新建项目microservicecloud-config-3344
POM.xml文件
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-config-3344artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-config-serverartifactId>
dependency>
<dependency>
<groupId>org.eclipse.jgitgroupId>
<artifactId>org.eclipse.jgitartifactId>
<version>4.10.0.201712302008-rversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
application.yml文件
server:
port: 3344
spring:
application:
name: microservicecloud-config
cloud:
config:
server:
git:
uri: git@github.com:luokangyuan/microservicecloud-config.git #GitHub上面的git仓库名字
主启动类
@SpringBootApplication
@EnableConfigServer
public class Config_3344_StartSpringCloudApp {
public static void main(String[] args) {
SpringApplication.run(Config_3344_StartSpringCloudApp.class, args);
}
}
修改host文件
127.0.0.1 config-3344.com
测试通过config微服务从GitHub上获取配置内容
启动服务3344,访问http://config-3344.com:3344/application-dev.yml,http://config-3344.com:3344/application-test.yml
本地新建microservicecloud-config-client.yml文件,并推送到github
server:
port: 8201
spring:
profiles: dev
application:
name: microservicecloud-config-client
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: microservicecloud-config-client
eureka:
client:
service-url:
defaultZone: http://eureka-test.com:7001/eureka/
新建项目microservicecloud-config-client-3355,pom.xml文件如下
<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.0modelVersion>
<parent>
<groupId>com.luo.springcloudgroupId>
<artifactId>microservicecloudartifactId>
<version>0.0.1-SNAPSHOTversion>
parent>
<artifactId>microservicecloud-config-client-3355artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-hystrixartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-configartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jettyartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>springloadedartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
dependency>
dependencies>
project>
新建bootstrap.yml文件
spring:
cloud:
config:
name: microservicecloud-config-client #需要从github上读取的资源名称,注意没有yml后缀名
profile: test #本次访问的配置项
label: master
uri: http://config-3344.com:3344 #本微服务启动后先去找3344号服务,通过SpringCloudConfig获取GitHub的服务地址
application.yml是用户级的资源配置文件,bootstrap.yml是系统级,优先级更高,保证不会被本地配置文件所覆盖
修改host文件,增加映射
127.0.0.1 client-config.com
新建测试controller,从github读取配置信息
package com.luo.springcloud.rest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConfigClientRest
{
@Value("${spring.application.name}")
private String applicationName;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaServers;
@Value("${server.port}")
private String port;
@RequestMapping("/config")
public String getConfig()
{
String str = "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
System.out.println("******str: " + str);
return "applicationName: " + applicationName + "\t eurekaServers:" + eurekaServers + "\t port: " + port;
}
}
新建主启动类
@SpringBootApplication
public class ConfigClient_3355_StartSpringCloudApp {
public static void main(String[] args) {
SpringApplication.run(ConfigClient_3355_StartSpringCloudApp.class, args);
}
}
测试
启动3344服务,启动3355服务,bootstrap.yml中的profile值是什么,决定从github上读取什么,ruguo
访问http://client-config.com:8201/config得到是github上的microservicecloud-config-client.yml文件中dev相关的配置信息
访问http://client-config.com:8202/config得到是github上的microservicecloud-config-client.yml文件中test相关的配置信息