SpringCloud Note [vaynexiao]

微服务

什么是微服务?
就目前而言,对于微服务,业界并没有一个统一的,标准的定义
但通常而言,微服务架构是一种架构模式,或者说是一种架构风格, 它提倡将单一的应用程序划分成一
组小的服务,每个服务运行在其独立的自己的进程内,服务之间互相协调,互相配置,为用户提供最终
价值。服务之间采用轻量级的通信机制互相沟通,每个服务都围绕着具体的业务进行构建,并且能够被
独立的部署到生产环境中,另外,应尽量避免统一的,集中式的服务管理机制,对具体的一个服务而
言,应根据业务上下文,选择合适的语言,工具对其进行构建,可以有一个非常轻量级的集中式管理来
协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储;

可能有的人觉得官方的话太过生涩,我们从技术维度来理解下:
微服务化的核心就是将传统的一站式应用,根据业务拆分成多个服务,彻底去耦合,
每一个微服务提供单个业务功能的服务,一个服务做一件事情,从技术角度看就是一种小而独立的处理过程,
类似进程的概念,能够自行单独启动或销毁。

微服务
强调的是服务的大小,他关注的是某一个点,是具体解决某一个问题/提供落地对应服务的一个服务应
用,狭义的看,可以看做是IDEA中的一个个微服务工程,或者Moudel

微服务架构
一种新的架构形式,Martin Fowler,2014提出!
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调,互相配
合,为用户提供最终价值。每个服务运行在其独立的进程中,服务于服务间采用轻量级的通信机制互相
协作,每个服务都围绕着具体的业务进行构建,并且能够被独立的部署到生产环境中,另外,应尽量避
免统一的,集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言,工
具对其进行构建。

优点
    每个服务足够内聚,足够小,代码容易理解,这样能聚焦一个指定的业务功能或业务需求;
    开发简单,开发效率提高,一个服务可能就是专一的只干一件事;
    微服务能够被小团队单独开发,这个小团队是2~5人的开发人员组成;
    微服务是松耦合的,是有功能意义的服务,无论是在开发阶段或部署阶段都是独立的。
    微服务能使用不同的语言开发。
    易于和第三方集成,微服务允许容易且灵活的方式集成自动部署,通过持续集成工具,如jenkins
    微服务容易被开发人员理解,修改和维护,这样小团队能够更关注自己的工作成果。无需通过合作才能体现价值。
    微服务允许你利用融合最新技术。
    微服务只是业务逻辑的代码,不会和 HTML,CSS 或其他界面混合
    每个微服务都有自己的存储能力,可以有自己的数据库,也可以有统一数据库

缺点:
    开发人员要处理分布式系统的复杂性
    多服务运维难度,随着服务的增加,运维的压力也在增大
    系统部署依赖
    服务间通信成本
    数据一致性
    系统集成测试
    性能监控....
为什么选择SpringCloud作为微服务架构
1、选型依据
    整体解决方案和框架成熟度
    社区热度
    可维护性
    学习曲线
2、当前各大IT公司用的微服务架构有哪些?
    阿里:dubbo+HFS
    京东:JSF
    新浪:Motan
    当当网 DubboX
    .......
3、各微服务框架对比

SpringCloud是什么
SpringCloud, 基于SpringBoot提供了一套微服务解决方案,包括服务注册与发现,配置中心,全链路监
控,服务网关,负载均衡,熔断器等组件,除了基于NetFlix的开源组件做高度抽象封装之外,还有一些
选型中立的开源组件。
SpringCloud利用SpringBoot的开发便利性,巧妙地简化了分布式系统基础设施的开发,SpringCloud为
开发人员提供了快速构建分布式系统的一些工具,包括配置管理,服务发现,断路器,路由,微代理,
事件总线,全局锁,决策竞选,分布式会话等等,他们都可以用SpringBoot的开发风格做到一键启动和
部署。
SpringBoot并没有重复造轮子,它只是将目前各家公司开发的比较成熟,经得起实际考研的服务框架组
合起来,通过SpringBoot风格进行再封装,屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套
简单易懂,易部署和易维护的分布式系统开发工具包
SpringCloud 是 分布式微服务架构下的一站式解决方案,是各个微服务架构落地技术的集合体,俗称微
服务全家桶。

SpringCloud和SpringBoot关系
SpringBoot专注于快速方便的开发单个个体微服务。
SpringCloud是关注全局的微服务协调整理治理框架,它将SpringBoot开发的一个个单体微服务整合并管理起来,
为各个微服务之间提供:配置管理,服务发现,断路器,路由,微代理,事件总线,全局锁,决策竞选,
分布式会话等等集成服务。
SpringBoot可以离开SpringClooud独立使用,开发项目,但是SpringCloud离不开SpringBoot,
属于依赖关系,SpringBoot专注于快速、方便的开发单个个体微服务,SpringCloud关注全局的服务治理框架

是一种架构模式

  • 将应用的每个功能放到一个独立的服务中,每个服务对应一个进程
  • 使用一组小型服务来开发单个应用,每个服务运行在独立的进程中,服务与服务之间通过HTTP的方式进行互相通信
  • 每个服务都是一个可独立替换和独立升级的软件单元, 并且能够被独立的部署到生产环境

优点:

  • 分而治之:单个服务功能内聚,复杂性低,方便团队的拆分和管理
  • 可伸缩:能够单独的对指定的服务进行伸缩
  • 迭代周期短:支持快速的迭代开发
  • 独立部署,独立开发

缺点:

  • 运维要求高:应用流程通常跨多个微服务,不易进行问题的定位
  • 分布式的复杂性:微服务需要使用分布式,而由于分布式本身的复杂性,导致微服务架构也变得复杂起来

SpringCloud简介

  • SpringCloud是一套完整的微服务解决方案,基于SpringBoot框架
  • SpringCloud是一系列框架的有序集合,它利用SpringBoot的开发便利性简化了分布式系统的开发
  • SpringCloud为开发人员提供了快速构建分布式系统的一些工具,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等
  1. 技术栈
    微服务之间通过HTTP的方式进行互相通信,此时Web API的设计就显得非常重要,会使用 Restful API设计方式

Restful

Representational State Transfer,简称为REST, 即表现层状态转化

资源 Resources
指的是网络上的某个数据, 如一个文件、一种服务等

表现层 Representational
资源的表现层,指的是资源的具体呈现形式,如HTML、JSON等

状态转化 State Transfer
指的是状态变化,通过HTTP方法来实现:
  GET 获取资源
  POST 新建资源
  PUT 更新资源
  DELETE 删除资源

简单来说,客户端通过HTTP方法对服务器的资源进行操作, 实现表现层状态转化

Restful 是目前最流行的 API 设计规范,用于 Web 数据接口的设计

Restful API设计原则:

  • 尽量将API 部署在一个专用的域名下,如 http://api.itany.com

  • API的版本应该在URL中体现,如 http://api.itany.com/v2

  • URL中不要使用动词,应使用资源名词,且使用名词的复数形式,如

功能 请求类型 URL
获取用户列表 GET http://api.baidu.com/v2/users
根据id获取用户 GET http://api.baidu.com/v2/users/id
添加用户 POST http://api.baidu.com/v2/users
根据id删除用户 DELETE http://api.baidu.com/v2/users/id
修改用户 PUT http://api.baidu.com/v2/users

注:简单来说,可以使用同一个 URL ,通过约定不同的 HTTP 方法来实施不同的业务

  • 服务器响应时返回JSON对象,应包含HTTP状态码、消息、结果等,如

{code:200,message:“success”,result:{id:1001,name:“tom”}}

  • 最好在返回的结果中提供链接, 指向其他的API方法,使得用户不用查文档, 就能知道下一步该怎么做

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9C4f34JM-1606228445791)(C:\Users\vaynexiao\AppData\Roaming\Typora\typora-user-images\image-20201121001119494.png)]

swagger2

通常情况下,我们会创建一份Restful API文档来记录所有的接口细节,
供其他开发人员使用提供的接口服务,但会存在以下的问题:
	• 接口众多,并且细节复杂
	• 需要根据接口的变化,不断修改API文档,非常麻烦,费时费力
Swagger2的出现就是为了解决上述的这些问题,减少创建API文档的工作量
	• 后端人员在代码里添加接口的说明内容,就能够生成可预览的API文档,无须再维护Word文档
	• 让维护文档和修改代码整合为一体,在修改代码逻辑的同时方便的修改文档说明
	• 提供了强大的页面测试功能,便于对接口进行测试
	
<!-- 核心依赖 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.9.2</version>
</dependency>
<!-- 只为了解决某个bug -->
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swaggerannotations</artifactId>
    <version>1.5.21</version>
</dependency>
<dependency>
    <groupId>io.swagger</groupId>
    <artifactId>swaggermodels</artifactId>
    <version>1.5.21</version>
</dependency>
//创建Swagger2配置类
@Configuration
@EnableSwagger2
public class Swagger2 {
    
    
        @Bean
        public Docket createRestApi() {
    
    
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    //.enable(false) //配置是否启用Swagger,如果是false,在浏览器将无法访问
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.yss.ms.admin"))
                    .paths(PathSelectors.any())
                    .build();
        }
        private ApiInfo apiInfo() {
    
    
            return new ApiInfoBuilder()
                    .title("服务:发布为daocke镜像,权限管理,用户管理,页面管理,日志 后台 APIs")
                    .description("服务:发布为daocke镜像,权限管理,用户管理,页面管理,日志 后台")
                    .termsOfServiceUrl("http://ip:port/xxx/xxx")
                    .contact("程序猿")
                    .version("1.0")
                    .build();
        }
    }
@Api:用在请求的类上,表示对类的说明
    tags="说明该类的作用,可以在UI界面上看到的注解"
    value="该参数没什么意义,在UI界面上也看到,所以不需要配置"

@ApiOperation:用在请求的方法上,说明方法的用途、作用
    value="说明方法的用途、作用"
    notes="方法的备注说明"

@ApiResponses:用于请求的方法上,表示一组响应
     @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:抛出异常的类
@ApiResponses({
    
    
    @ApiResponse(code=400,message="请求参数没填好"),
    @ApiResponse(code=404,message="请求路径没有或页面跳转路径不对")
})

@ApiImplicitParams:用在请求的方法上,包含一组参数说明
     @ApiImplicitParam:用在 @ApiImplicitParams 注解中,指定一个请求参数的配置信息       
        name:参数名
        value:参数的汉字说明、解释
        required:参数是否必须传
        paramType:参数放在哪个地方
            · header --> 请求参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · body(不常用)
            · form(不常用)    
        dataType:参数类型,默认String,其它值dataType="Integer"       
        defaultValue:参数的默认值
@ApiImplicitParams({
    
    
    @ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),
    @ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),
    @ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")
})
    
@ApiModel 标注在模型Model上,比如User Dept对模型进行说明
	@ApiModelProperty
@ApiModel(description= "返回响应数据")
public class RestMessage implements Serializable{
    
    
    @ApiModelProperty(value = "是否成功")
    private boolean success=true;
}

@ApiIgnore 标注在类或方法上,表示忽略这个类或方法
    
http://ip:port/swaggerui.html

Eureka

父模块 子模块 是通过yml文件产生关联

父模块 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.baidu</groupId>
    <artifactId>cpt</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>api</module>
        <module>provider-dept-8001</module>
        <module>comsumer-dept-8002</module>
        <module>eureka-7001</module>
        <module>eureka-7002</module>
        <module>eureka-7003</module>
    </modules>

    <packaging>pom</packaging>

    <properties>
        <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>Hoxton.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 注意:springboot是2.2.x,2.3.x   springcloud匹配的是H版本
                官方给出的兼容表:https://spring.io/projects/spring-cloud -->
            <!-- spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.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.6</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.4</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</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>
        </dependencies>
    </dependencyManagement>

</project>

api模块

Dept.java

api模块只提供一个实体类Dept

public class Dept implements Serializable {
    
    

    private Long deptno; //主键
    private String dname; //部门名称
    // 来自哪个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存到多个不同的 数据库
    private String db_source;

    public Dept() {
    
    
    }

    public Dept(String dname) {
    
    
        this.dname = dname;
    }

    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;
    }

    public Dept(Long deptno, String dname, String db_source) {
    
    
        this.deptno = deptno;
        this.dname = dname;
        this.db_source = db_source;
    }

}

provider-dept-8001模块

sql

CREATE TABLE `dept` (
  `deptno` int(11) NOT NULL AUTO_INCREMENT,
  `dname` varchar(255) DEFAULT NULL,
  `db_source` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

insert into dept (dname, db_source) values ('开发部', DATABASE());

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.baidu</groupId>
    <artifactId>provider-dept-8001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <!--引入自定义的模块,我们就可以使用这个模块中的类了-->
        <dependency>
            <groupId>com.baidu</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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.projectlombok</groupId>
            <artifactId>lombok</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-test</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.4.RELEASE</version>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>5</source>
                    <target>5</target>
                </configuration>
            </plugin>
        </plugins>
        <finalName>robot</finalName>
        <resources>
            <resource>
                <directory>src/main/webapp</directory>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

application.yml

server:
 port: 8001

#mybatis的配置
mybatis:
  type-aliases-package: com.baidu.springCloud.pojo
  # 此后mapper.xml可以直接简写类名为User,不用写全类名
  # 注意和mybatis-config.xml中typeAliases不一样,这个会默认类名首字母小写
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/mapper/*.xml

#spring的相关配置
spring:
  application:
    name: springcloud-provider-dept
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource # 数据源
    driver-class-name: org.gjt.mm.mysql.Driver # mysql驱动
    url: jdbc:mysql://localhost:3306/api #数据库名称
    username: root
    password: 123456
    dbcp2:
      min-idle: 5  #数据库连接池的最小维持连接数
      initial-size: 5 #初始化连接数
      max-total: 5 #最大连接数
      max-wait-millis: 200  #等待连接获取的最大超时时间

#eureka配置
eureka:
 client:
  service-url:
   defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
 instance:
   instance-id: provider-dept-8001
   prefer-ip-address: true # true访问路径可以显示IP地址

info:
  author: vaunexiao

mybatis-config.xml

resource 下

<?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>

DeptMapper.xml

resource - mapper 下

<?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.baidu.cpt.dao.DeptDao">

    <insert id="addDept" parameterType="com.baidu.cpt.pojo.Dept">
        insert into dept (dname,db_source) values (#{dname},DATABASE());
    </insert>

    <select id="queryById" resultType="com.baidu.cpt.pojo.Dept" parameterType="Long">
        select deptno,dname,db_source from dept where deptno = #{deptno};
    </select>

    <select id="queryAll" resultType="com.baidu.cpt.pojo.Dept">
        select deptno,dname,db_source from dept;
    </select>

</mapper>

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.baidu</groupId>
    <artifactId>provider-dept-8001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <!--引入自定义的模块,我们就可以使用这个模块中的类了-->
        <dependency>
            <groupId>com.baidu</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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.projectlombok</groupId>
            <artifactId>lombok</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-test</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.4.RELEASE</version>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>5</source>
                    <target>5</target>
                </configuration>
            </plugin>
        </plugins>
        <finalName>robot</finalName>
        <resources>
            <resource>
                <directory>src/main/webapp</directory>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>

DeptController.java

package com.baidu.cpt.controller;

import com.baidu.cpt.service.DeptService;
import com.baidu.cpt.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/dept")
public class DeptController {
    
    

    @Autowired
    private DeptService service;

    @Autowired
    private DiscoveryClient client;

    // 如果参数是放在请求体中,传入后台的话,那么后台要用@RequestBody才能接收到
    @PostMapping("/add")
    public boolean addDept(@RequestBody Dept dept) {
    
    
        return service.addDept(dept);
    }

    @GetMapping("/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
    
    
        return service.queryById(id);
    }

    @GetMapping("/list")
    public List<Dept> queryAll() {
    
    
        return service.queryAll();
    }

    @RequestMapping("/hello")
    public String hello(){
    
    
        return "hello";
    }

    @GetMapping("/discovery")
    public Object discovery(){
    
    
        //获得微服务列表清单
        List<String> list = client.getServices();
        System.out.println("client.getServices()==>"+list);
        //得到一个具体的微服务!
        List<ServiceInstance> serviceInstanceList = client.getInstances("springcloud-provider-dept");
        for (ServiceInstance serviceInstance : serviceInstanceList) {
    
    
            System.out.println(
                    serviceInstance.getServiceId()+"\t"+ serviceInstance.getHost()+"\t"+
                            serviceInstance.getPort()+"\t"+ serviceInstance.getUri() );
        }
        return this.client;
    }

}

DeptDao.java

package com.baidu.cpt.dao;

import com.baidu.cpt.pojo.Dept;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;

@Mapper
public interface DeptDao {
    
    
    public boolean addDept(Dept dept); //添加一个部门
    public Dept queryById(Long id); //根据id查询部门
    public List<Dept> queryAll(); // 查询所有部门
}

DeptService.java

package com.baidu.cpt.service;

import com.baidu.cpt.pojo.Dept;
import java.util.List;

public interface DeptService {
    
    
    public boolean addDept(Dept dept); //添加一个部门
    public Dept queryById(Long id); //根据id查询部门
    public List<Dept> queryAll(); //查询所有部门
}

DeptServiceImpl.java

package com.baidu.cpt.service;

import com.baidu.cpt.dao.DeptDao;
import com.baidu.cpt.pojo.Dept;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class DeptServiceImpl implements DeptService{
    
    

    @Autowired
    private DeptDao deptDao;

    @Override
    public boolean addDept(Dept dept) {
    
    
        return deptDao.addDept(dept);
    }

    @Override
    public Dept queryById(Long id) {
    
    
        return deptDao.queryById(id);
    }

    @Override
    public List<Dept> queryAll() {
    
    
        return deptDao.queryAll();
    }

}

ProviderDept8001.java

@SpringBootApplication
@EnableEurekaClient //启动后会自动注册到eureka中
//@EnableDiscoveryClient // 这个不加似乎也可以
public class ProviderDept8001 {
    
    
    public static void main(String[] args) {
    
    
        //启动测试前,应先启动provider模块
        SpringApplication.run(ProviderDept8001.class,args);
    }
}

comsumer-dept-8002模块

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>comsumer-dept-8002</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.baidu</groupId>
            <artifactId>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>

application.yml

server:
 port: 8002

ConfigBean.java

package com.baidu.cpt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration //相当于spring的applicationContext.xml来配置bean
public class ConfigBean {
    
    
	//RestTemplate这个类没有被spring默认配置为bean,需要手动使用@Configuration的配置类来配置成bean
    @Bean
    public RestTemplate getRestTemplate(){
    
    
        return new RestTemplate();
    }

}

DeptConsumerController.java

import org.springframework.web.client.RestTemplate;

@RestController
public class DeptConsumerController {
    
    
    //理解:消费者,不应该有service层
    // 使用RestTemplate访问restful接口非常的简单粗暴且无脑
    // (url,requestMap,ResponseBean.class) 这三个参数分别代表
    // REST请求地址,请求参数,Http响应转换 被 转换成的对象类型
    @Autowired
    private RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://localhost:8001";

    @RequestMapping("/consumer/dept/add")
    public boolean add(Dept dept){
    
    
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add", dept, Boolean.class);
    }

    @RequestMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id, Dept.class);
        //return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/{id}"+, Dept.class, id);//没测试过
    }

    @RequestMapping("/consumer/dept/list")
    public List<Dept> list(){
    
    
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list", List.class);
    }
//    RestTemplate提供了多种便捷访问远程Http服务的方法,是一种简单便捷的访问restful服务模板 类,
//    是Spring提供的用于访问Rest服务的客户端模板工具集
//
//    使用RestTemplate访问restful接口非常的简单粗暴且无脑
//    (url,requsetMap,ResponseBean.class) 这三个参数分别代表REST请求地址,请求参数, Http响应转换 被 转换成的对象类型
}

ComsumerDept8002.java

package com.baidu.cpt;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ComsumerDept8002 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(ComsumerDept8002.class,args);
    }
}

eureka-7001模块

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-7001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>

</project>

application.yml

server:
 port: 7001

eureka:
 instance:
  hostname: eureka7001.com #eureka服务端的实例名称
  prefer-ip-address: true  #eureka页面鼠标悬浮实例名上后可以显示IP地址
 client:
  register-with-eureka: false  # 是否将自己注册到Eureka服务器中,本身是服务器,无 需注册
  fetch-registry: false # false表示自己端就是注册中心,我的职责就是维护服务实例,并 不需要去检索服务
  service-url: # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个defaultZone地址
   # 单机: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
   # 集群(关联):
   defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
   # Spring Cloud Eureka 常用配置及说明:https://www.cnblogs.com/li3807/p/7282492.html

EurekaServer7001.java

@SpringBootApplication
@EnableEurekaServer //EurekaServer服务器端启动类,接受其他微服务注册进来!
public class EurekaServer7001 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServer7001.class,args);
    }
}

eureka-7002模块

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-7002</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>

</project>

applicatuion.yml

server:
  port: 7002

#Eureka配置
eureka:
  instance:
    hostname: eureka7002.com #Eureka服务端的实例名称
  client:
    register-with-eureka: false # 表示是否向eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
    service-url:  # 监控页面
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/

EurekaServer7002.java

@SpringBootApplication
@EnableEurekaServer //EurekaServer服务器端启动类,接受其他微服务注册进来!
public class EurekaServer7002 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServer7002.class,args);
    }
}

eureka-7003模块

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>cpt</artifactId>
        <groupId>com.baidu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>eureka-7003</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.6.RELEASE</version>
        </dependency>
    </dependencies>

</project>

applicatuion.yml

server:
  port: 7003

#Eureka配置
eureka:
  instance:
    hostname: eureka7003.com #Eureka服务端的实例名称
  client:
    register-with-eureka: false # 表示是否向eureka注册中心注册自己
    fetch-registry: false #fetch-registry如果为false,则表示自己为注册中心
    service-url:  # 监控页面~
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

EurekaServer7003.java

@SpringBootApplication
@EnableEurekaServer //EurekaServer服务器端启动类,接受其他微服务注册进来!
public class EurekaServer7003 {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServer7003.class,args);
    }
}

自我保护机制

修改一个服务名,故意制造错误,出现红色文本警告信息

一句话总结:某时刻某一个微服务不可以用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存!

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注
销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可
能变得非常危险了–因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过 自我保护
机制 来解决这个问题–当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故
障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的
信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。

在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心
跳数重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式。自我保护模式是一种应对网络异常的安全保护措施,它的设计哲学就是宁可保留错误的服务注册信息(健康的微服务和不健康的微服务都会保留),也不盲目注销任何可能健康的服务实例。一句话:好死不如赖活着。可以让Eureka集群更加的健壮和稳定。

在SpringCloud中,可以使用 eureka.server.enable-self-preservation = false 禁用自我保护模式 【不推荐关闭自我保护机制】

Eureka集群

修改host文件,且编写多个eureka模块,互相之间关联,成为集群
C:\Windows\System32\drivers\etc

127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com

CAP原则

RDBMS (Mysql、Oracle、sqlServer)===>ACID
NoSQL(redis、mongdb)===> CAP
    
ACID是什么?
    A(Atomicity)原子性
    C(Consistency) 一致性
    I (Isolation)隔离性
    D(Durability)持久性 
    
CAP是什么?
    C(Consistency)强一致性
    A(Availability)可用性
    P(Partition tolerance)分区容错性

CAP理论的核心
	一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。
	根据CAP原理,将NoSQL数据库分成了满足CA原则,满足CP原则和满足AP原则三大类:
        CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差
        CP:满足一致性,分区容错性的系统,通常性能不是特别高
        AP:满足可用性,分区容错性的系统,通常可能对一致性要求低一些

对比Zookeeper

由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。
	Zookeeper保证的是CP;
	Eureka保证的是AP;

Zookeeper保证的是CP
    当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服
    务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种
    情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在
    于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注
    册服务瘫痪。在云部署的环境下,因为网络问题使得zk集群失去master节点是较大概率会发生的事件,
    虽然服务最终能够恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

Eureka保证的是AP
    Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂
    掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个
    Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册
    服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在
    15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此
    时会出现以下几种情况:
		1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
		2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依
然可用)
		3. 当网络稳定时,当前实例新的注册信息会被同步到其他节点中

因此,Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪

Feign

RestTemplate调用其他模块接口地址是写死的,使用Feigh解决
Feign是一个HTTP客户端,可以更快捷、优雅地调用HTTP服务,使编写HTTPClient变得更简单。
在Spring Cloud中使用Feign非常简单,只需要创建一个接口,然后在接口上添加一些注解就可以了。

Feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。
Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
只需要创建一个接口,然后添加注解即可!
feign ,主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问两种方法

1. 微服务名字 【ribbon】
2. 接口和注解 【feign 】

1 编辑pom.xml文件,配置Feign和Eureka-Client依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2 编辑启动类,启用Eureka客户端

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.xxx.xxx") // 启用Feign客户端,扫描指定包下所有的feign注解
public class CloudConsumer8080Application {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(CloudConsumer8080Application.class, args);
    }
}

3 创建interface并使用注解配置

@FeignClient("user-provider") // 调用的服务名,到Eureka中寻找对应的微服务
@Service
public interface UserService {
    
    

   @GetMapping("/users/{id}")
   public ResponseResult getUser(@PathVariable(value = "id") Integer id);

   @PostMapping("/users")
   public ResponseResult postUser(@RequestParam("name") String name,@RequestParam("pwd") String pwd);

   @PutMapping("/users")
   public ResponseResult putUser(@RequestParam Map<String,Object> map);
}

4 Controller

@RestController
public class DeptConsumerController {
    
    
    
  @Autowired
  private DeptClientService service = null;
    
  @RequestMapping("/consumer/dept/add")
  public boolean add(Dept dept){
    
    
    return this.service.addDept(dept);
  }
  @RequestMapping("/consumer/dept/get/{id}")
  public Dept get(@PathVariable("id") Long id){
    
    
    return this.service.queryById(id);
  }
  @RequestMapping("/consumer/dept/list")
  public List<Dept> list(){
    
    
    return this.service.queryAll();
  }
    
}

feign传递对象参数的解决方式:

  • 方式一:将对象参数拆为多个简单类型参数,且必须添加@RequestParam注解
  • 方式二:使用Map替代对象参数,且必须添加@RequestParam注解

通过Feign直接找到服务接口,由于在进行服务调用的时候融合了Ribbon技术,所以也支持负载均衡作用!
feign其实不是做负载均衡的,负载均衡是ribbon的功能,feign只是集成了ribbon而已,但是负载均衡的功能
还是feign内置的ribbon再做,而不是feign。默认轮询方式。

Feign的作用的替代RestTemplate,性能比较低,但是简化了请求的编写,也使代码可读性提高

Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调
用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避
免级联故障,以提高分布式系统的弹性。
    
“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险
丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出
调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避
免了故障在分布式系统中的蔓延,乃至雪崩.

服务雪崩:在微服务架构中服务之间会相互调用和依赖,如果某个服务发生故障,可能会导致多个服务故障,从而导致整个系统故障(类似蝴蝶效应)

解决方式1:熔断(服务端)

在这里插入图片描述

当服务出现不可用或响应超时时,为了防止整个系统出现雪崩, 暂时停止对该服务的调用,直接返回一个结果,
快速释放资源。
如果检测到目标服务情况好转,则恢复对目标服务的调用。(可理解为将故障服务暂时隔离)
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,
快速返回 错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud
框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,
缺省是5秒内20次调用失败就会启动熔断机制。
熔断机制的注解是 @HystrixCommand。

1, pom

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix</artifactId>
	<version>1.4.7.RELEASE</version>
</dependency>

2,Controller @HystrixCommand(fallbackMethod = “方法名”)

@RestController
public class DeptController {
    
    
  @Autowired
  private DeptService service;
  //一旦调用服务方法失败并抛出了错误信息后
  // 会自动调用HystrixCommand标注好的fallbackMethod调用类中指定方法
  @GetMapping("/dept/get/{id}")
  @HystrixCommand(fallbackMethod = "processHystrix_Get")
  public Dept get(@PathVariable("id") Long id) {
    
    
    Dept dept = service.queryById(id);
    if (dept==null){
    
    
      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");
      }
}

3,启动类增加注解

@EnableCircuitBreaker //对hystrix 熔断机制的支持

解决方式2:降级(客户端)

在这里插入图片描述

1, implements FallbackFactory 针对单个接口

@Component //千万不要忘记
public class DeptClientServiceFallbackFactory implements
FallbackFactory<DeptClientService> {
    
    
  @Override
  public DeptClientService create(Throwable throwable) {
    
    
    return new DeptClientService() {
    
    
      @Override
      public Dept queryById(Long id) {
    
    
        return new Dept().setDeptno(id)
           .setDname("该id:"+id+"没有对应的信息,Consumer客户端提供的降级信息,此刻服务Provider已经关闭")
           .setDb_source("no this database in MySQL");
     }
      @Override
      public List<Dept> queryAll() {
    
    
        return null;
     }
      @Override
      public boolean addDept(Dept dept) {
    
    
        return false;
     }
   };
 }
}

2,接口增加注解

@FeignClient(value="PROVIDER-01",fallbackFactory=DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
    
    

3,application.yml,启用断路器

feign:
 hystrix:
  enabled: true

测试

当服务提供者不可用或出现异常时,比如 int a = 5/0,会暂时停止对该服务的调用

Dashboard

是一个独立项目

除了隔离依赖服务的调用以外,Hystrix还提供了准实时的调用监控(Hystrix Dashboard),Hystrix会
持续地记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒
执行多少请求,多少成功,多少失败等等。

Netflix通过hystrix-metrics-event-stream项目实现了对以上指标的监控,
SpringCloud也提供了HystrixDashboard的整合,对监控内容转化成可视化界面!

pom

<!--Hystrix-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix</artifactId>
	<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
	<version>1.4.7.RELEASE</version>
</dependency>

application.yaml

server:
 port: 9001

主启动类改名 + 新注解@EnableHystrixDashboard

@SpringBootApplication
@EnableHystrixDashboard
public class DeptConsumerDashBoardApp9001 {
    
    
	public static void main(String[] args) {
    
    
		SpringApplication.run(DeptConsumerDashBoardApp9001.class,args);
	}
}

所有的Provider微服务提供类(8001/8002/8003) 都需要监控依赖配置

<!--actuator监控信息完善-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

http://localhost:9001/hystrix
在这里插入图片描述

Zuul

Zuul本身也是一个项目,也会注册到Eureka

对请求的统一管理和处理,比如隐藏真实ip 端口 服务名等。

Zuul是一个路由网关Gateway,包含两大功能:
对请求的路由:将外部的请求转发到具体的微服务实例上,是实现外部访问统一入口的基础
对请求的过滤:对请求的处理过程进行干预,是实现请求校验、服务聚合等功能的基础

将Zuul和Eureka进行整合,把Zuul自身注册为Eureka服务治理下的应用,
同时从Eureka中获得其他微服务的信息,对于微服务的访问都要通过Zuul进行跳转

pom.xml 文件,配置依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>springcloud-starter-netflix-eureka-client</artifactId>
	<version>1.4.7.RELEASE</version> 
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>springcloud-starter-netflix-zuul</artifactId>
	<version>1.4.7.RELEASE</version>
</dependency>

启动类,@EnableZuulProxy

@SpringBootApplication
@EnableZuulProxy // 启用Zuul,此注解是加强版,针对同时使用到eureka的情况
public class CloudZuul9001Application {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(CloudZuul9001Application.class, args);
    }
}

此时

application.yml,配置eureka和zuul

server:
    port: 6001
spring:
    application:
        name: zuulgateway
eureka:
    client:
        serviceurl:
            defaultZone: http://localhost:7001/eureka/
zuul: # 路由相关配置
    prefix: /v2 # 对每个微服务指定唯一的任意key值,比如v2版本号
    ignored-services: springcloud-provider-dept  
    # 不能再使用这个服务名访问,只可以用配置的path
    # ignored-services: "*" # 通配符*, 隐藏全部
    routes:  # 配置路由表
    user: # 对于每个微服务,可以自定义一个唯一的key值,该值可以任意指定
        path: /mydept/** # springcloud-provider-dept服务的路径全部转换为mydept
        serviceId: springcloud-provider-dept # 服务id

不用路由 :http://localhost:8001/dept/get/2
使用路由 :http://localhost:8001/mydept/dept/get/2

前面的localhost:8001也可以使用域名屏蔽

Ribbon

Ribbon是一套客户端负载均衡的工具,用于实现微服务的负载均衡 Load Balance
Ribbon不需要独立部署,Feign集成了Ribbon,自动的实现了负载均衡,那还需要引入ribbon依赖吗

搭建Provider集群
不能使用tempPlate传固定地址开放问了,要根据服务名(可以使用Feigh)然后eureka配置了Ribbon后动态选择一个服务方。
拷贝 cloud-provider-8001 为 cloud-provider-8002 和 cloud-provide-r8003

pom

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-ribbon</artifactId>
  <version>1.4.7.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

consumer 用Feigh无需设置,如果给Config类的Bean TempPlate加注解@LoadBalanced

@Bean
@LoadBalanced //开启负载均衡的功能
RestTemplate restTemplate() {
    
    
    return new RestTemplate();
}
//为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】
https://www.cnblogs.com/yourbatman/p/11532729.html

修改主启动类的类名(也要注册到eureka,所以@EnableEurekaClient)

@RibbonClient(name="provider", configuration=MySelfRule.class)

application.yml配置:端口号和instanceid不同 name相同

server:
 port: 8002 # 不同的端口
 
spring:
 application:
  name: user-provider # 相同的服务名
  
 eureka:
  client:
   service-url:
    defaultzone: http://localhost:7001/eureka/
   instance:
    instance-id: user-provider8002 # 不同的实例id

默认使用的是 轮询策略

常见的策略:

  • 轮询 (RoundRobinRule) (默认)
  • 随机 (RandomRule)
  • 响应时间权重(WeightedResponseTimeRule)响应时间越短的服务器被选中的可能性大
  • 并发量最小可用(BestAvailableRule)选取最少并发量请求的服务器

改变负载均衡策略

在Zuul服务中,通过配置类指定要应用的负载均衡策略

@Configuration
public class RibbonConfig {
    
    
    @Bean
    public IRule myRule(){
    
    
        return new RandomRule();
    }
}

Config

在这里插入图片描述

server

1、新建 maven 项目

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-eureka</artifactId>
	<version>1.4.7.RELEASE</version>
</dependency>
<!-- spring cloud config 服务端包 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

2,application.yml

server:
  port: 3399  
spring:
  application:
    name: config-single-server  # 应用名称
  cloud:
     config:
        server:
          git:
            uri: https://github.com/huzhicheng/config-only-a-demo # http的git项目路径
            #username: github # 登录账号
            #password: github # 登录密码
            #default-label: master # 配置文件分支
            #search-paths: config  # 配置文件所在根目录

3、启动类增加注解@EnableConfigServer

4,测试

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

{application} 就是应用名称,对应到配置文件上来,就是配置文件的名称部分,例如我上面创建的配置文件。

{profile} 就是配置文件的版本,我们的项目有开发版本、测试环境版本、生产环境版本,
对应到配置文件上来就是以 application-{profile}.yml 加以区分,
例如application-dev.yml、application-sit.yml、application-prod.yml。

{label} 表示 git 分支,默认是 master 分支,如果项目是以分支做区分也是可以的,
那就可以通过不同的 label 来控制访问不同的配置文件了。

上面的 5 条规则中,我们只看前三条,因为我这里的配置文件都是 yml 格式的。
根据这三条规则,我们可以通过以下地址查看配置文件内容:
http://localhost:3301/config-single-client/dev/master
http://localhost:3301/config-single-client/prod
http://localhost:3301/config-single-client-dev.yml
http://localhost:3301/config-single-client-prod.yml
http://localhost:3301/master/config-single-client-prod.yml

通过访问以上地址,如果可以正常返回数据,则说明配置中心服务端一切正常。

client

1.pom

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2 远程的client-provider-yml
在这里插入图片描述

3 本地项目yml,这一步表示和server通信

在这里插入图片描述
写一个controller,可以测试获取的信息是否拿到

@RestController
public class ConfigClientController {
    
    

    @Value("${spring.application.name}")
    private String applicationName;

    @Value("${eureka.client.service-url.defaultZone}")
    private String eurekaServer;

    @Value("${server.port}")
    private String port;

    @RequestMapping("/config")
    public String getConfig(){
    
    
        return "applicationName:"+applicationName +
         "eurekaServer:"+eurekaServer + "port:"+port;
    }
}

猜你喜欢

转载自blog.csdn.net/vayne_xiao/article/details/110099282