微服务架构是一种架构模式,它提倡将单一应用程序划分为一组小的服务,每个服务运行在自己独立的进程中,服务之间互相配合,互相协调,为用户提供最终价值。服务之间通过轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API,SpringCloud是restful,dubble是RPC),每个服务都能围绕具体业务进行构建,并且能被独立的部署到生产环境,类生产环境等,可以使用不同的语言来编写服务,也可以使用不同的数据存储。
微服务的核心就是将传统的一站式应用,根据业务拆分为一个一个的服务,彻底的去耦合,每个微服务提供单个业务功能的服务,一个服务做一件事,能够单独启动和销毁,有自己独立的数据库。
微服务关注的是一个点,是具体解决某个问题,提供落地对应服务的一个服务应用,狭义的讲,可以看做eclipse里的一个个微服务工程或者module。一个大的系统可能对应几百个微服务,专业的事情交给专业的模块来做。微服务强调的是个体,每个个体完成具体的任务或功能。一个微服务就是一个独立的进程
微服务架构强调的是整体,描述个体微服务之间如何协调,调用,微服务强调的是个体
SpringCloud:基于SpringBoot的一套微服务解决方案,利用SpringBooot的开发遍历性,简化了分布式系统基础设施的开发,SpringCloud为开发人员提供了快速构建分布式系统的一些工具;包括配置管理、服务发现、断路器、路由、微代理、事件总线等等。
SpringCloud和SpringBoot的关系:
SpringBoot关注的是一个一个的个体的服务,SpringCloud是分布式微服务架构下的一站式解决方案,是各个微服务架构的落地技术的集合体,俗称微服务全家桶。
springBoot关注微观,springCloud 关注宏观,springcloud依赖于springBoot。
SpringCloud英文文档
SringCloud中文文档
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<type>pom</type>
<scope>provided</scope>
</dependency>
1:新建一个maven project父工程(microservicecloud)
父工程作用:定义POM文件,将后续各个子模块公用的jar包等统一提出来,类似一个抽象父类;父工程用于聚合子过程,所以packaging选pom
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.0.4</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.31</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
父工程下新建一个maven module,打包方式选择 jar创建成功,在父工程pom文件中会 出现如下内容:
<modules>
<module>mocroservicecloud-api</module>
</modules>
在子工程pom文件中添加依赖:
<dependencies>
<!-- 当前Module需要用到的jar包,按自己需求添加,如果父类已经包含了,可以不用写版本号 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
在子工程microservicecloud-api中新建类:com.test.springcloud.entities.Dept,属性如下,借助上边pom文件中添加的lombok依赖包,可以通过添加注解,来代替手动生成get、set、无参和全参构造方法;
微服务实体类,需实现序列化接口
@AllArgsConstructor //全参构造方法
@NoArgsConstructor //无参构造方法
@Data //get/set方法
@Accessors(chain = true) //链式访问
public class Dept implements Serializable {
private Long deptno; // 主键
private String dname; // 部门名称
private String db_source;// 来自那个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库
}
选中子工程microservicecloud-api ,执行mvn clean和mvn install,给其他模块引用,达到通用的目的 ,以后其他子模块用到Dept这个类,直接引用本模块即可。
父工程下新建一个maven module,打包方式选择 jar创建成功,在父工程pom文件中会 出现如下内容:
<modules>
<module>mocroservicecloud-provider-dept-8001</module>
</modules>
在新建子工程pom文件中添加依赖:
<dependencies>
<!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
<dependency>
<groupId>com.test.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 将微服务provider侧注册进eureka -->
<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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>com.test.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
在src\main下新建文件夹 resources,resources下新建文件application.yml
文件内容如下:
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路径
type-aliases-package: com.test.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: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
在src\main\resources下新建mybatis文件夹,在mybatis文件夹下新建mybatis-cfg.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="cacheEnabled" value="true" /><!-- 二级缓存开启 -->
</settings>
</configuration>
在src\mian下新建dao包,在dao包下新建DeptDao接口内容如下 :
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DeptDao {
public boolean addDept(Dept dept);
public Dept findById(Long id);
public List<Dept> findAll();
在mybatis文件夹下新建mapper文件夹,文件夹下新建DeptMapper.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.test.springcloud.dao.DeptDao">
<select id="findById" resultType="com.test.springcloud.entities.Dept" parameterType="long">
select deptno,dname,db_source from dept where deptno=#{deptno};
</select>
<select id="findAll" resultType="com.test.springcloud.entities.Dept">
select deptno,dname,db_source from dept;
</select>
<insert id="addDept" parameterType="com.test.springcloud.entities.Dept">
INSERT INTO dept(dname,db_source) VALUES(#{dname},DATABASE());
</insert>
</mapper>
在src/main下新建 service目录,新进DeptService接口,如下:
public interface DeptService {
public boolean add(Dept dept);
public Dept get(Long id);
public List<Dept> list();
}
service下新建impl文件夹并创建实现类DeptServiceImpl实现DeptService接口:
@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<Dept> list()
{
return dao.findAll();
}
}
src/main下新建controller文件夹,创建DeptController类,内容如下:
@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<Dept> list()
{
return service.list();
}
@RestController = @Controller+@ResponseBody
在com.test.springcloud下新建主启动类DeptProvider8001_App.java,内容如下:
@SpringBootApplication
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class,args);
}
}
启动主启动类DeptProvider8001_App.java。访问即可查询
右键父工程,创建一个maven module(microservicecloud-consumer-dept-80)
pom文件中添加依赖
<dependencies>
<dependency><!-- 自己定义的api -->
<groupId>com.test.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
src\main下新建resources目录,创建application.yml文件,内容如下:
server:
port: 80
在src\main下新建包com.test.springcloud.cfgbeans,在包下新建ConfigBean.java配置类,添加@Configuration注解,相当于applicationContext.xml配置文件,内容如下:
@Configuration
public class ConfigBean {
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
新建com.test.springcloud.controller包,创建DeptController_Consumer类,内容如下:
@RestController
public class DeptController_Consumer {
/**
* 此类为消费者,所以不应该有Service层,service是服务提供者应该关注的东西,消费者应该直接调用
* 调用服务提供者提供的service,需要使用spring提供的RestTemplate类
* RestTemplate提供了多种便捷访问HTTP服务的方法,是一种简单便捷的访问Restful服务的模板类,
* 是spring提供的用于访问Rest服务的客户端模板工具集
*
* 使用restTemplate访问restful接口非常简单
* (url,requestMap,ResponseBean.class)这三个参数分别代表
* REST请求地址,请求参数,HTTP响应转换被转换成的对象类型(返回值类型)
*/
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 List<Dept> list(){
return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
}
}
创建主启动类DeptConsumer80_App
@SpringBootApplication
public class DeptConsumer80_App
{
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_App.class,args);
}
}
先后启动服务提供者和调用者两个微服务,输入客户端访问地址,测试。
Eureka主管服务的发现与注册,只需要使用服务的标识符,就可以访问到服务(服务只需要注册进Eureka,其他服务就可以通过名字找到这个服务),功能类似 dubbo的注册中心,比如Zookeeper,Eureka相当于管理 服务的一个花名册。
Eureka Server作为服务注册功能的服务器,是服务的注册中心,系统中的其他微服务,通过Eureca的客户端连接到Eureka Server并维持心跳,这样系统的维护人员 就可以通过Eureka Server来监控系统中的各个微服务是否正常运行,SpringCloud的其他模块(比如Zuul)就可以通过Eureka Server来发现系统中的其他微服务,并执行相关逻辑。采用C-S设计,即客户端和服务端;
Eureka包含两个组件:Eureka Server 和Eureka Client,Eureka Server 提供消息注册服务,各个节点启动后,会在Eureka Server 进行注册,这样Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点信息可以在界面中直观的看到。
Service Provider服务的提供方将自身注册到Eureka,从而使服务消费方能找到,Service Consumer服务消费方从Eureka获取注册列表,从而能够消费服务。
父工程下创建子module工程:microservicecloud-eureka-7001,添加POM依赖,如下:
<dependencies>
<!--eureka-server服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
src\main下新建resources目录,创建application.yml文件,文件内容如下:
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称,此时本机且只有一个Eureka,可以用localhost
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机),告诉其他服务入驻时来这
在com.test.springcloud下创建主启动类EurekaServer7001_App,在主启动类上添加@EnableEurekaServer注解,开启Eureka服务
@SpringBootApplication
@EnableEurekaServer//Eureca服务端启动类,接受其他服务注册进来
public class EurekaServer7001_App {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001_App.class,args);
}
}
启动EurekaServer7001_App,浏览器访问localhost:7001出现如下界面,说明成功:
No instances available:没有服务被发现
在8001POM文件 中添加如下依赖:
<!-- 将微服务provider侧注册进eureka -->
<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>
修改8001的yml配置文件,添加如下内容:
eureka:
client: #客户端注册进eureka服务列表内
service-url:
defaultZone: http://localhost:7001/eureka #告诉8001入驻地址是啥,这个地址和7001的yml中配置的地址对应
在8001主启动类添加@EnableEurekaClient注解
@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进Eraeka服务中
public class DeptProvider8001_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider8001_App.class,args);
}
}
先后启动7001和8001,测试
成功注册;application名字对应8001yml配置文件中配置的服务名。
Eureka的自我保护:十多分钟没有任何服务访问,即没有心跳,就会出现。
修改status名字(主机名称修改)
修改8001yml配置,添加如下内容:
修改后效果:
修改当鼠标放在status链接上时的左下角提示(即访问status路径时的IP信息提示)
在8001yml配置文件中添加如下内容:
修改微服务info内容详细信息,即点击 status链接后展示的信息
添加8001POM依赖:
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
父工程中添加构建build信息:
<!--所有src/main/resources目录下的配置文件,以$开头$结尾的-->
<build>
<finalName>microservicecloud</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
修改8001的yml,添加如下内容:
info:
app.name: test-microservicecloud
company.name: www.test.com
build.artifactId: $project.artifactId$
build.version: $project.version$
自我保护描述:某时刻某个微服务不可用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存。
默认情况下,如果EurekaServer在一定时间内没有接受到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒),但是当网络分区发生故障时,微服务与EurekaServer之间无法正常通信,以上行为就会变得非常危险了,因为微服务其实是健康的,此时不应该注销这个微服务,Eureka通过自我保护模式来解决这个问题,当EurekaServer节点在短时间内 丢失过多客户端时(可能发生了网络分区故障),那么这个EurekaServer节点就会进入自我保护模式,一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不在删除注册表中的数据(也就是不会注销任何微服务),当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。
down1:不可用的一个
up1:可用的一个
禁用自我保护机制,在7001yml中添加如下内容即可:
服务发现:让其他微服务也可以获得该服务的信息
在8001Controller类添加如下代码:
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
public Object discovery()
{
List<String> list = client.getServices();//获取Eureka注册的所有服务
System.out.println("**********" + list);
List<ServiceInstance> 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;
}
8001主启动类添加
@EnableDiscoveryClient//服务发现
注解
先后启动Eureka7001和DeptProvider8001,访问http://localhost:8001/dept/discovery
可见,8001成功访问到了自己注册进Erueka的服务的一些信息。
在80服务controller类添加如下代码:
// 测试@EnableDiscoveryClient,消费端可以调用服务发现
@RequestMapping(value = "/consumer/dept/discovery")
public Object discovery()
{
return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class);
}
先后启动7001/8001/80工程,访问localhost:80/consumer/dept/discovery测试
即8001做了一个对外暴露服务信息的接口,供客户端80访问
集群:在不同服务器上配置相同的服务
新建子module微服务microservicecloud-eureka-7002和microservicecloud-eureka-7003
粘贴7001的pom依赖
<dependencies>
<!--eureka-server服务端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
按照7001添加7002和7003的主启动类
修改yml中映射,之前只有一个Eureka,所以Eureka的实例主机名可以叫localhost,现在三个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
分别修改7001 、7002 、7003的yml配置
集群要互相关联,即7001上写7002和7003,7002上写7001和7003,7003上写7001和7002
其中7001修改如下,7002和7003与此类似:
# hostname: localhost改为
hostname: eureka7001.com #eureka服务端的实例名称,在C:\Windows\System32\drivers\etc下的hosts文件中映射
#单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址(单机)。
改为
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
修改8001yml配置,添加另外两个集群地址,修改如下:
# defaultZone: http://localhost:7001/eureka #告诉8001入驻地址是啥,这个地址和7001的yml中配置的地址对应
改为: defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
分别启动7001/7002/7003/8001/80测试
访问:http://eureka7001.com:7001/
Eureka与zookeeper区别
传统的关系型数据库:ACID
A:原子性
C:一致性
I:独立性
D:持久性
NOSQL数据库:CAP
C:强一致性
A:可用性
P:分区容错性
任何一个分布式系统无法同时满足CAP,只能三选二
对于分布式系统,当前的网络硬件肯定会出现延迟 丢包等问题,所以P必须满足,所以只能选择AP和CP
Eureka:AP
Zoomkeeper:CP
在80客户端微服务POM文件中,添加Ribbon依赖,如下:
<!-- Ribbon相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
修改80的yml文件,添加Eurrka配置,使得80不再直接调用8001,而是通过Eureka调用:
添加内容如下:
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
在80微服务的ConfigBean的getRestTemplate()方法上添加@LoadBalanced注解,表明客户端访问时需自带负载均衡
在80主启动类上添加@EnableEurekaClient注解
修改80客户端访问类,把通过地址访问改为通过名称访问,修改如下:
//private static final String REST_URL_PREFIX ="http://localhost:8001";
private static final String REST_URL_PREFIX ="http://MICROSERVICECLOUD-DEPT";//eureka上的微服务名
先后启动7001、7002、7003、8001、80测试
访问http://localhost/consumer/dept/list,确认改为通过 微服务名访问后,依旧可用
新建microserviccecloud-provider-dept-8002和microserviccecloud-provider-dept-8003子工程
按照8001POM文件,添加依赖,拷贝配置及Java类,修改主启动类名、yml文件中port及数据库名和instanceid,新建clouddb02和clouddb03数据库,不可以修改yml中对外暴露的服务名,要保证三个服务对外暴露的服务名相同,要修改的内容如下:
改端口
server:
port: 8002
改数据库
url: jdbc:mysql://localhost:3306/clouddb02 # 数据库名称
改服务ID
instance:
instance-id: microservicecloud-dept8002
先启动三个Eureka集群配置区,再启动三个Dept微服务提供者,再启动consumer消费者,消费者通过Ribbon完成负载均衡并访问微服务提供者。
访问可发现查询到的dept来源于不同数据库,默认轮循。
http://localhost/consumer/dept/list,刷新页面,发现查询数据来源于不同数据库
根据特定算法中从服务类表中选取一个要访问的服务。
cloud结合Ribbon自带7种算法
在80客户端ConfigBean中添加如下内容:
@Bean
public IRule myRule(){
return new RandomRule();//用我们选择的随机算法替换轮训
}
刷新页面,可见由轮循变为随机从三个库中查
修改80,在主启动类上添加@RibbonClient注解,在MySelfRule类里写自定义规则,需事先IRule接口。
/**在启动该微服务的时候就能去加载我们自定义的Ribbon配置类,从而使配置生效。
形如:@RibbonClient(name=""MICROSERVICECLOUD-DEPT",Configuration=MySelfRule.class)*/
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration = MySelfRule.class)
public class DeptConsumer80_App
即MyRule类不能在com.test.springcloud包及其子包下(因为主启动类在com.test.springcloud包下):
新建com.test.myrule包,在包下新建类MySelfRule.java,内容如下:
@Configuration
public class MySelfRule {
@Bean
public IRule myRule(){
return new RandomRule_ZY();
}
}
新建RandomRule_ZY类,内容如下:
/**
* 自定义每个执行五次
*/
public class RandomRule_ZY extends AbstractLoadBalancerRule
{
// total = 0 // 当total==5以后,我们指针才能往下走,
// index = 0 // 当前对外提供服务的服务器地址,
// total需要重新置为零,但是已经达到过一个5次,我们的index = 1
// 分析:我们5次,但是微服务只有8001 8002 8003 三台
//
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<Server> upList = lb.getReachableServers();//当前在线的服务
List<Server> allList = lb.getAllServers();//所有服务
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
// int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
// server = upList.get(index);
// 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) {
/*
* The only time this should happen is if the server list were somehow trimmed.
* This is a transient condition. Retry after yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Overr ide
public Server choose(Object key)
{
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig)
{
// TODO Auto-generated method stub
}
}
访问http://localhost/consumer/dept/list,可以看到按自定义的规则,每个服务访问五次;
Feign是一个声明式WebService客户端,使用Feign能让编写Web Service客户端更加 简单,它的使用方法是定义一个接口,然后在上面添加注释,同时也支持JAX-RS标准的注释,Feign也支持可拔插式的编码器和解码器,Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准和HttpMessageConverters,feign可以与Eureka和Ribbon组合使用以支持负载均衡。
Feign是一个声明式的Web服务客户端,使得编写Web服务客户端非常容易。只需要创建一个接口,然后在上边添加注释即可。
仿照microservicecloud-consumer-dept-80创建microserviceclou-consumer-dept-feign子工程
修改主启动类名,删除主启动类上的@RibbonClient注解
在新建 feign工程添加80POM中的依赖,额外再添加feign依赖,如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
主启动类如下:
@SpringBootApplication
@EnableEurekaClient
public class DeptConsumer80_Feign_App
{
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_Feign_App.class,args);
}
}
修改API模块内容,api是通用的,修改加入feign后,其他模块就都可以通用啦
修改api工程的POM文件,添加feign依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
在API工程中新建接口并添加注解com.test.springcloud.DeptClientService.java,内容如下:
@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<Dept> list();
@RequestMapping(value = "/dept/add", method = RequestMethod.POST)
public boolean add(Dept dept);
}
对API工程进行重新mvn clean \install;
返回microserviceclou-consumer-dept-feign工程,修改controller,添加上一步新建的DeptClientService接口,代码如下:
@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<Dept> list()
{
return this.service.list();
}
@RequestMapping(value = "/consumer/dept/add")
public Object add(Dept dept)
{
return this.service.add(dept);
}
}
修改feign主启动类,添加后内容如下:
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.test.springcloud"})
//@ComponentScan("com.test.springcloud")
public class DeptConsumer80_Feign_App
{
public static void main(String[] args) {
SpringApplication.run(DeptConsumer80_Feign_App.class,args);
}
}
依次启动7001/7002/7003/8001/8002/8003/feign进行测试:http://localhost/consumer/dept/list
可见Feign开发较之前80的开发更为简便,不再需要使用 RestTemplate 类,直接使用接口+注解开发
服务报错时执行适当的兜底方法,保证不报错。
参考8001,新建子工程,服务端microservicecloud-provider-dept-hystrix-8001,复制8001的pom,包等内容
在pom文件中添加hystrix依赖,如下:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
修改yml配置文件,添加如下内容:
修改controller类,如下:
/**
* 不想每个方法靠添加HystrixCommand注解来处理熔断,在api接口上添加fallbackFactory,出了错就会找fallbackFactory,统一处理
*/
@RestController
public class DeptController {
@Autowired
private DeptService service = null;
@RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
//一旦调用服务方法失败并抛出了错误信息后,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "processHystrix_Get")
public Dept get(@PathVariable("id") Long id)
{
Dept dept = this.service.get(id);
if (null == dept) {
//如果抛出异常,根据 @HystrixCommand(fallbackMethod = "processHystrix_Get")注解
// 可知会调用下边的processHystrix_Get方法
throw new RuntimeException("该ID:" + id + "没有没有对应的信息");
}
return dept;
}
public Dept processHystrix_Get(@PathVariable("id") Long id)
{
Dept dept = new Dept();
dept.setDeptno(id);
dept.setDname("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand");
dept.setDb_source("no this database in MySQL");
return dept;
}
}
在主启动类添加@EnableCircuitBreaker注先后启动7001/7002/7003/Hystrix/80
访问http://localhost/consumer/dept/get/112进行测试
可见,对于超出数据库记录的查询,返回一个适当结果。
修改API子工程,根据已有的DeptClientService接口,新建一个实现了FallbackFactory接口的类DeptClientServiceFallBackFactory
在Service接口上添加如下内容:
@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory=DeptClientServiceFallbackFactory.class)
新建DeptClientServiceFallBackFactory类,内容如下:
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory<DeptClientService> {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept get(long id) {
Dept dept = new Dept();
dept.setDeptno(id);
dept.setDname("该ID:" + id + "没有没有对应的信息,consumer客户端提供的降级信息,此刻服务provider已经关闭");
dept.setDb_source("no this database in MySQL");
return dept;
}
@Override
public List<Dept> list() {
return null;
}
@Override
public boolean add(Dept dept) {
return false;
}
};
}
}
即,当service接口出错时,会来这个类执行(不需要每个方法都加注解标识出错执行哪个方法了,直接在包含所有方法的接口上标识,然后处理类封装为一个类 ),解耦,执行api的mvn clean/install
修改feign工程的yml文件,添加如下内容
先后启动7001/7002/7003/8001/feign进行测试http://localhost/consumer/dept/get/2,故意关闭8001,测试
关掉服务显示
此时服务端provider已经down掉了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息,而不会挂起耗死服务器。
服务熔断是报异常了,调用兜底方法。服务降级是资源不够用了。
新建子工程microservicecloud-provider-dept-hystrix-8001添加如下POM依赖:
<dependencies>
<!-- 自己定义的api -->
<dependency>
<groupId>com.yx</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改后立即生效,热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- Ribbon相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- feign相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- hystrix和 hystrix-dashboard相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
创建application.yml文件,内容如下:
server:
port: 9001
主启动类修改名字且添加注解
@EnableHystrixDashboard,如下:
@SpringBootApplication
@EnableHystrixDashboard
public class DeptProvider9001_Hystrix_Dashboard_App {
public static void main(String[] args) {
SpringApplication.run(DeptProvider9001_Hystrix_Dashboard_App.class,args);
}
}
8001/8002/8003POM 文件中添加监控依赖
<!-- actuator监控信息完善 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
先启动Hystrix-dashboard,访问localhost:9001看能否正常访问
继续启动7001/7002/7003和hystrix-8001和hystrix-dashboard一共六个服务,如图测试:
填监控地址:
看图时看七色一圈一线
频繁刷新http://localhost:8001/dept/get/2地址,可见线和圈在随频率变化
新建工程microservicecloud-zuul-gateway-9527添加POM依赖
<dependencies>
<!-- zuul路由网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- actuator监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- hystrix容错 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<!-- 日常标配 -->
<dependency>
<groupId>com.test.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 热部署插件 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
添加application.yml文件,如下:
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
app.name: test-microcloud
company.name: www.test.com
build.artifactId: $project.artifactId$
build.version: $project.version$
创建主启动类,添加@EnableZuulProxy注解
@SpringBootApplication
@EnableZuulProxy
public class Zuul_9527_StartSpringCloudApp {
public static void main(String[] args) {
SpringApplication.run(Zuul_9527_StartSpringCloudApp.class,args);
}
}
C:\Windows\System32\drivers\etc的hosts文件中添加127.0.0.1 myzuul.com
先后启动7001、7002、7003、8001、zuul测试
不用路由查
上边用zuul访问时,对外暴露了自己真实的微服务名称
在yml文件中添加如下内容:
zuul:
routes:
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**
访问测试
zuul:
ignored-services: microservicecloud-dept
上边是一个一个批处理,批处理在yml中添加:
zuul:
ignored-services: "*"
设置统一公共前缀
prefix: /test
总体如下:
zuul:
#ignored-services: microservicecloud-dept
prefix: /yx
ignored-services: "*"
routes:
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**
测试加前缀,不加不能访问了
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200504203843621.png