springcloud与微服务
springcloud 与 微服务

父工程(按需导入依赖)
<!--打包-->
    <packaging>pom</packaging>
    <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.13</junit.version>
        <lombok.version>1.18.12</lombok.version>
        <log4j.version>1.2.17</log4j.version>
        <logback.version>1.2.3</logback.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--springcloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--springboot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.1.4.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--mysql-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.19</version>
            </dependency>
            <!--连接池-->
            <!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>3.4.1</version>
            </dependency>
            <!--springboot 启动器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>
            <!--log4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
新建api module暴露实体类
按需导入依赖
<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
建表例子
-- ----------------------------
-- Table structure for dept
-- ----------------------------
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `dept_no` bigint(0) NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `db_source` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`dept_no`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO dept(dept_name,db_source) VALUES('部门01',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门02',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门03',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门04',DATABASE());
新建pojo
@Data
@NoArgsConstructor
@Accessors(chain = true)//链式写法 new User().set().set()
public class Dept implements Serializable { //网络传输要序列化 Dept实体类 orm 类表关系映射
    private Long deptNo;
    private String deptName;
    //这个数据存在那个数据库,微服务,一个服务对应一个数据库,同一个信息也可能存在不同的数据库
    private String dbSource;
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
}
新建服务提供者(Mavan 约定>配置>编码)
按需导入依赖
   <dependencies>
        <dependency>
            <groupId>com.fuck</groupId>
            <artifactId>fuck-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.zaxxer</groupId>
            <artifactId>HikariCP</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-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--jetty-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
配置application.yaml
server:
  port: 8001
mybatis:
  type-aliases-package: com.antake.pojo
  mapper-locations: classpath:mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true
spring:
  application:
    name: fuck-provide-dept
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456
编写Mapper
@Mapper
public interface DeptMapper {
    boolean addDept(Dept dept);
    Dept queryById(Long id);
    List<Dept> queryAll();
}
边写对应的Mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.antake.mapper.DeptMapper">
    <insert id="addDept">
        insert into dept(dept_name,db_source)
        values (#{deptName},database())
    </insert>
    <select id="queryById" resultType="dept" parameterType="Long">
        select dept_no,dept_name,db_source from dept where dept_no = #{deptNo}
    </select>
    <select id="queryAll" resultType="dept">
        select dept_no,dept_name,db_source from dept
    </select>
</mapper>
编写service
public interface DeptService {
    boolean addDept(Dept dept);
    Dept queryById(Long id);
    List<Dept> queryAll();
}
@Service
@Transactional(rollbackFor = Exception.class) //有mybatisconfig必须开启启用事务支持,不配置就就在启动类上加
public class DeptServiceImpl implements DeptService {
    @Autowired
    DeptMapper deptMapper;
    @Override
    public boolean addDept(Dept dept) {
        return deptMapper.addDept(dept);
    }
    @Override
    public Dept queryById(Long id) {
        return deptMapper.queryById(id);
    }
    @Override
    public List<Dept> queryAll() {
        return deptMapper.queryAll();
    }
}
编写controller
@RestController
@RequestMapping("/dept")
public class DeptController {
    @Autowired
    DeptService deptService;
    @PostMapping("/add")
    public boolean addDept(@RequestBody Dept dept){
        return deptService.addDept(dept);
    }
    @GetMapping("/get/{id}")
    public Dept get(@PathVariable("id")Long id){
        return deptService.queryById(id);
    }
    @GetMapping("/list")
    public List<Dept> queryAll(){
        return deptService.queryAll();
    }
}
编写启动类
@SpringBootApplication
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}
结果

新建消费者(不该有service层,通过RestTemplate来调用service)
添加依赖
    <dependencies>
        <dependency>
            <groupId>com.fuck</groupId>
            <artifactId>fuck-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>
配置application.yaml
server:
  port: 80
要用RestTemplate就把他注入容器
//config包下面
@Configuration
public class ConfigBean {
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
新建ConsuemerController
为什么方法上面要用包装类而不用基本数据类型?对外暴露的接口,进行数据传输的时,如果不能保证全部不为null,最好用包装类,这样不容易出异常
@RestController
@RequestMapping("/consumer")
public class DeptConsumerController {
    //(url,请求实体Map,Class<T> responseType)
    @Autowired
    RestTemplate restTemplate;//提供多种便捷的远程访问方式
    private static final String REST_URL_PREFIX="http://localhost:8081";
    @RequestMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    }
    @PostMapping("/dept/add")
    public Boolean add(@RequestBody Dept dept){
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }
    @GetMapping("/dept/list}")
    public List<Dept> list(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }
}
编写启动类
@SpringBootApplication
public class DeptConsumer_80 {
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer_80.class,args);
    }
}
结果

注册中心eureka
添加依赖
<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka-server</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
编写配置文件 application.yaml
server:
  port: 7001
  #Eureka配置
eureka:
  instance:
    hostname: localhost #eureka服务端的实例名字
  client:
    register-with-eureka: false #表示是否向eureka注册中心注册自己
    fetch-registry: false #如果为false,则表示自己为注册中心
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
编写启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}
启用了注册中心之后,就需要给服务提供者添加配置
首先添加Eureka client的依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
注册服务,在application.yaml中添加注册中心的地址
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
开启注解
@SpringBootApplication
@EnableEurekaClient
public class DeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProvider_8001.class,args);
    }
}
结果

可以修改一些信息(例如修改provider信息)
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: this is fucking provider at port 8001
PS:自我保护机制

为了通过注册中心直观的检测到provider的状态和信息(点击以下链接),可以做以下步骤

添加依赖(完善监控信息)
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
配置信息
info:
  app.name: this is fucking app name
  company.name: this is your fucking company name
结果

获取一些服务提供者的消息(团队开发要用,类似于swagger)
- @RestController
 @RequestMapping("/dept")
 public class DeptController {
 @Autowired
 DeptService deptService;
 @Autowired
 DiscoveryClient discoveryClient;
 @PostMapping("/add")
 public boolean addDept(@RequestBody Dept dept){
 return deptService.addDept(dept);
 }
 @GetMapping("/get/{id}")
 public Dept get(@PathVariable("id")Long id){
 return deptService.queryById(id);
 }
 @GetMapping("/list")
 public List<Dept> queryAll(){
 return deptService.queryAll();
 }
 //注册进来的微服务,获取一些消息
 @GetMapping("/discovery")
 public Object discovery(){
 //获得微服务的清单
 List<String> services = discoveryClient.getServices();
 System.out.println(services);
 List<ServiceInstance> instances = discoveryClient.getInstances("fuck-provide-dept");
 for (ServiceInstance instance : instances) {
 System.out.println(instance.getHost()+"\t"
 +instance.getPort()+"\t"
 +instance.getUri()+"\t"
 +instance.getInstanceId());
 }
 return this.discoveryClient;
 }
 }
 
- 添加注解,使服务发现生效 - @EnableDiscoveryClient
 
- 打印的信息  
eureka集群(解决其中一个奔溃了还有备用方案)
新建两个注册中心 过程如上
讲集群联系起来
		
更改eureka yaml
server:
  port: 7001
  #Eureka配置
eureka:
  instance:
    hostname: eureka01.com #eureka服务端的实例名字
  client:
    register-with-eureka: false #表示是否向eureka注册中心注册自己
    fetch-registry: false #如果为false,则表示自己为注册中心
    service-url:
      #单机defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      defaultZone: http://eureka02.com:7002/eureka/,http://eureka03.com:7003/eureka/ #集群
更改provider yaml 让其在三个注册中心注册
server:
  port: 8001
mybatis:
  type-aliases-package: com.antake.pojo
  mapper-locations: classpath:mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true
spring:
  application:
    name: fuck-provide-dept
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db01?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8
    username: root
    password: 123456
eureka:
  client:
    service-url:
      defaultZone: http://eureka01.com:7001/eureka/,http://eureka02.com:7002/eureka/,http://eureka03.com:7003/eureka/
  instance:
    instance-id: this is fucking provider at port 8001
info:
  app.name: this is fucking app name
  company.name: this is your fucking company name
CAP原则
回顾CAP原则
- RDBMS (Mysql、Oracle、SQL Server) ===>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:满足可用性,分区容错性的系统,通常可能对一致性要求低一些
 
作为服务注册中心,Eureka比Zookeeper好在哪里?面试题
著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)、P(容错性)。
由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡
- Zookeeper保证的是CP;
- Eureka保证的是AP;
- 中途产生的问题
- Zookeeper是怎么选举的?https://www.cnblogs.com/shuaiandjun/p/9383655.html
 


Ribbon
什么是Ribbon

Ribbon能干什么?

集成Ribbon(在客户端也就是消费者里面)
添加依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
        <!--需要eureka客户端,因为要发现服务-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
配置Eureka (yaml)
server:
  port: 80
eureka:
  client:
    register-with-eureka: false #消费者无需注册自己
    service-url:
      defaultZone: http://eureka02.com:7002/eureka/,http://eureka01.com:7001/eureka/,http://eureka03.com:7003/eureka/
启动Eureka注解
@EnableEurekaClient
配置负载均衡,实现RestTemplate
@Configuration
public class ConfigBean {
    //配置负载均衡,实现RestTemplate
    @Bean
    @LoadBalanced //就是这个注解,实现负载均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
修改该controller
@RestController
@RequestMapping("/consumer")
public class DeptConsumerController {
    //(url,请求实体Map,Class<T> responseType)
    @Autowired
    RestTemplate restTemplate;//提供多种便捷的远程访问方式
    //通过Ribbon实现的时候,不应该写死下面这个,而是应该通过服务名拿到服务,再通过负载均衡实现服务选择
    //private static final String REST_URL_PREFIX="http://localhost:8001";
    private static final String REST_URL_PREFIX="http://fuck-provide-dept";
    @GetMapping("/dept/get/{id}")
    public Dept get(@PathVariable("id")Long id){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/get/"+id,Dept.class);
    }
    @PostMapping("/dept/add")
    public Boolean add(@RequestBody Dept dept){
        return restTemplate.postForObject(REST_URL_PREFIX+"/dept/add",dept,Boolean.class);
    }
    @GetMapping("/dept/list")
    public List<Dept> list(){
        return restTemplate.getForObject(REST_URL_PREFIX+"/dept/list",List.class);
    }
}
结果
- 开启两个Eureka 一个服务提供者 一个消费者  



- 但是在这里并不能体现出负载均衡,因为只有一个服务提供者,也没有多个数据库
得到一个结论
- Eureka和Ribbon整合之后,客户端只需要调用服务即可,不用关心IP地址和端口号,只管用就行,不管怎么实现的
Ribbon实现负载均衡
新增两个数据库,作为数据源
/*db02*/
create DATABASE db02;
use db02;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `dept_no` bigint(0) NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `db_source` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`dept_no`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO dept(dept_name,db_source) VALUES('部门01',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门02',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门03',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门04',DATABASE());
/*db03*/
create DATABASE db03;
use db03;
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `dept_no` bigint(0) NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `db_source` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`dept_no`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO dept(dept_name,db_source) VALUES('部门01',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门02',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门03',DATABASE());
INSERT INTO dept(dept_name,db_source) VALUES('部门04',DATABASE());
新增两个服务提供者步骤如上 (注意修改数据库)

自定义负载均衡算法
- RoundRobinRUle:轮询
- RandomRule:随机
- AvailabilityFilteringRule:会先过滤掉,跳闸,访问故障的服务,对剩下的的进行轮询
- RetryRule:先按照轮询获取服务,如果服务获取失败,则会在规定时间内进行重试
- Weight Round Robin:加权轮询
- Least Connection:最少连接数
- Least Connection Slow Start Time:最少连接数慢启动时间
- Weighted Least Connection:加权最少连接
- Agent Based Adaptive Balancing:基于代理的自适应负载均衡
- Fixed Weight:固定权重
- Weighted Response:加权响应
- Source IP Hash:源IP哈希
在consumer修改config(注意: 自定义的算法类不能放在主启动类的包及其子包下。)
- 因为主启动类使用了@SpringBootApplication注解,而这个注解又默认添加了@ComponentScan注解【只要使用了@SpringBootApplication注解就使用了@ComponentScan注解】。下面是@SpringBootApplication的@ComponentScan注解源码: - @ComponentScan(excludeFilters = {
 @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
 - 可以看到,这个注解会把添加了注解的当前类所在的当前包及其子包都进行扫描,并且使用所扫描到的注解的默认配置。 
 一旦扫描到@RibbonClient注解,它就会默认使用Spring提供的负载均衡算法(这里是为什么呢?因为@RibbonClient注解又实现了@Import(RibbonClientConfigurationRegistrar.class)注解,引入了spring提供的默认配置算法),因此我们自己的配置就不会起作用。
@Bean//随机的算法
    public IRule getRule(){
        return new RandomRule();
    }
使用自己的算法
- 修改之后的启动类 - package com.antake; import com.myRule.MyRule;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 import org.springframework.cloud.netflix.ribbon.RibbonClient; @SpringBootApplication
 @EnableEurekaClient
 //在微服务启动的时候就能去加载我们自定义的Ribbon类
 @RibbonClient(name = "fuck-provide-dept",configuration = MyRule.class)
 public class DeptConsumer_80 {
 public static void main(String[] args) {
 SpringApplication.run(DeptConsumer_80.class,args);
 }
 }
 
- 修改的之后的config.ConfigBean并无变化 
- 增加的配置 - package com.myRule; import com.netflix.loadbalancer.IRule;
 import com.netflix.loadbalancer.RandomRule;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration; @Configuration
 public class MyRule {
 @Bean
 public IRule getRule(){
 return new RandomRule();
 }
 }
 
Feign负载均衡
简介
Feign是声明式的web service客户端,它让微服务之间的调用变得更加简单了,类似于controller调用service。SpringCloud集成了Ribbon和Eureka,可以使用Feign时提供负载均衡的的HTTP客户端。(代码可读性变高了,但是因为加了一层,所以效率变低了)
只需要创建一个接口,然后添加注解即可!
feign,主要是社区,大家都习惯面向接口编程。这个是很多开发人员的规范。调用微服务访问的两种方法
- 微服务名字【ribbon】
- 接口和注解【feign】
Feign能干什么?

Feign集成了Ribbon
- 利用Ribbon维护了MicroServiceCloud-Dept的服务列表信息,并且通过轮询实现了客户端的负载均衡,而与Ribbon不同的是,通过Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
怎么实现
接口和注解
- 首先引入依赖(在之前ribbon的消费者consumer上) - <dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-feign</artifactId>
 <version>1.4.7.RELEASE</version>
 </dependency>
 
- 在api上新增接口(需要引入feign的依赖) - package com.antake.service; import com.antake.pojo.Dept;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.stereotype.Service;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody; import java.util.List;
 //@Service
 //@Component
 @FeignClient(value = "fuck-provide-dept")//value填写服务名称,这样就可以不用在消费者中写死
 public interface DeptService {
 @GetMapping("/dept/get/{id}")
 Dept getDeptById(@PathVariable("id") Long id);
 @GetMapping("/dept/list")
 List<Dept> queryAll();
 @PostMapping("/dept/add")
 boolean add(@RequestBody Dept dept);
 }
 - 【】这样配置 deptService 会报错,因为没有扫描到这个Service,需要在启用类上添加@EnableFeignClients(basePackages={"com.antake.service"}),这里诞生出一个问题,直接在Service接口上添加@Service也可以,并不知道有什么区别核问题,有待深究 - 又仔细想了一下,回想Spring的自动注入,能注入是因为该实例已经存在于spring的容器中,就可以反推,添加了@FeignClient并不会自动添加到容器中,而使用@Service就可以将其纳入到spring的容器管理当中,用@EnableFeignClients不配置basePackages的时候会默认扫描所有包中有@FeignClient的,然后将其纳入容器管理中,虽然两者都能做到能使用service,但是应该用@EnableFeignClients,因为@Service并不能起到fegin客户端的作用 
- 用feign调用和ribbon主要的区别(还有重要的就是新建了一个service)  
分布式系统面临的问题
复杂的分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将有不可避免的失败!
服务雪崩
 多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用了其他微服务,这就是所谓的”扇出“,如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是所谓的”雪崩效应“。
 对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟之内饱和,比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不取消整个应用程序或系统。
 这时候我们就需要弃车保帅
什么是Hystrix(加载服务提供者上面)

官网https://github.com/Netflix/Hystrix/
服务熔断(服务端修改 provider)

步骤
新建一个项目,直接拷贝之前的服务提供者即可,也可以在原有的代码上进行修改
导入依赖
<!--友情提示:也可以用直接用新的架包,还是推荐用springcloud推荐的,毕竟兼容性更好-->
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
最好修改一下application.yaml 以示区分(可选)
instance-id: this is fucking provider contains hystrix at port 8001
修改controller开启功能
@RestController
@RequestMapping("/dept")
public class DeptController {
    @Autowired
    DeptService deptService;
    @GetMapping("/get/{id}")
    @HystrixCommand(fallbackMethod = "hystrixGet")
    public Dept get(@PathVariable("id")Long id){
        Dept dept = deptService.queryById(id);
        if (dept==null){
            //异常捕获,虽然该服务没有问题,但是不排除该服务的调用者不出现问题
            //例如该服务的调用者一直在等待dept的返回,返回null就会出现问题
            //据说这个代码最值钱,而不是crud
            throw new RuntimeException("id=>"+id+"\t没有查询到对应的信息");
            //直接抛出异常好吗?显然是不好的,即使是该服务的调用者捕获了异常但是没有,结果
            //此时可以调用备用方法来处理
        }
        return deptService.queryById(id);
    }
    public Dept hystrixGet(@PathVariable("id")Long id){
        return new Dept().setDeptNo(id).setDeptName("id=>"+id+"\t没有查询到对应的信息").setDbSource("no data in database");
    }
}
添加注册,是程序支持熔断
@EnableCircuitBreaker
prefer-ip-address=true #显示ip
服务降级(客户端修改 consumer)



直接关闭了该服务提供者,但是访问还是可以拿到降级信息

对比

dashboard服务监控(consumer)
添加依赖(在consumer的基础上再添加)

服务端口9001
开启监控,增加Servlet(主意一点,必须要有监控的依赖 actuator)
- 问:有了springMVC,为什么还要用servlet?有了servlet3的注解,为什么还要使用ServletRegistrationBean注入的方式? - 使用场景:在有些场景下,比如我们要使用hystrix-dashboard,这时候就需要注入HystrixMetricsStreamServlet(第三方的servlet),该servlet是hystrix的组件。 



解释



Zuul路由网关
什么是Zuul?

Zuul能干嘛?
- 路由
- 过滤
官网文档:https://github.com/Netflix/zuul
步骤
新建项目
<!--在监控面板的基础上再加-->
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zuul</artifactId>
            <version>1.4.7.RELEASE</version>
        </dependency>
配置application.yaml
server:
  port: 9527
spring:
  application:
    name: springcloud-zuul
eureka:
  client:
    service-url:
      defaultZone: http://eureka01.com:7001/eureka/,http://eureka02.com:7002/eureka/,http://eureka03.com:7003/eureka/
  instance:
    instance-id: zuul9527
    prefer-ip-address: true #显示Ip
info:
  app.name: antake's springcloud
  company.name: my company
  version: 1.0.0
编写启动类
@SpringBootApplication
@EnableZuulProxy
public class FuckZuul_9527 {
    public static void main(String[] args) {
        SpringApplication.run(FuckZuul_9527.class);
    }
}
隐藏真实的微服务名称
zuul:
  ignored-services: fuck-provide-dept #使哪些名称访问失效,set #路径替换的微服务名称 ”*“ 隐藏
  routes:
    mydept.serverId: fuck-provide-dept #微服务名称
    mydept.path: /mydept/**
  prefix: /antake #公共前缀 加了前缀必须通过前缀去访问,没有就报错

SpringCloud config分布式配置
分布式系统面临的--配置文件的问题

什么是SpringCloud config分布式配置中心

SpringCloud config分布式配置中心能干嘛?

SpringCloud config分布式配置中心与github整合
 由于SpringCloud Config默认使用Git来存储配置文件(也有其他方式,比如支持SVN和本地文件),但是最推荐的还是Git,而且使用的是http/https访问的形式
git使用
- git add .
- git status
- git commit -m "message"
- git push
新建config server

添加依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
    </dependencies>
配置yaml
server:
  port: 3344
spring:
  application:
    name: fuck-config-server
  #连接远程仓库
  cloud:
    config:
      server:
        git:
          uri: https://gitee.com/antake/lightfoods-admin-config.git
编写启动类
@SpringBootApplication
@EnableConfigServer
public class ConfigServer_3344 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigServer_3344.class,args);
    }
}
HTTP服务具有以下格式的资源:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

读配置

出现问题

解决办法,将config-server版本降低
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
    <version>2.1.4.RELEASE</version>
</dependency>
成功


有什么用?
config-client就可以通过config-server去访问git,拿到自己的配置文件
新建client
添加yaml config-dept-provider.yaml 并上传到gitee
spring:
    profiles:
        active: dev
---
server:
  port: 8201
spring:
  profiles: dev
  application:
    name: fuck-provide-dept
eureka:
  client:
    service-url:
      defaultZone: http://eureka01.com:7001/eureka/,http://eureka02.com:7002/eureka/,http://eureka03.com:7003/eureka/
---
server:
  port: 8202
spring:
  profiles: test
  application:
    name: fuck-provide-dept
eureka:
  client:
    service-url:
      defaultZone: http://eureka01.com:7001/eureka/,http://eureka02.com:7002/eureka/,http://eureka03.com:7003/eureka/

添加依赖
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <version>2.1.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
    </dependencies>
新建bootstrap.yaml
bootstrap 系统级别的配置 application 用户级别的配置
spring:
  cloud:
    config:
      uri: http://localhost:3344 # 服务端
      name: config-dept-provider #从git获取的文件名,不需要后缀
      profile: dev #用什么环境
      label: master #从那个分支去拿
application.yaml
spring:
  application:
    name: dept-provider-3355-test
新建一个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;
    @GetMapping("/config")
    public String getConfig(){
        return "applicationName:"+applicationName+"\teurekaServer:"+eurekaServer+"\tport:"+port;
    }
}
编写启动类
@SpringBootApplication
public class DeptProviderConfigClient_3355 {
    public static void main(String[] args) {
        SpringApplication.run(DeptProviderConfigClient_3355.class,args);
    }
}
启动 server和client查看结果

结果
- 此时发现application.yaml里面配置的应用名未生效,依旧证明了bootstrap的优先级高于application
Springcloud 总结

springcloud与微服务的更多相关文章
- 用SpringCloud进行微服务架构演进
		在<架构师必须要知道的阿里的中台战略与微服务> 中已经阐明选择SpringCloud进行微服务架构实现中台战略,因此下面介绍SpringCloud的一些内容,SpringCloud已经出来 ... 
- 基于Spring-Cloud的微服务框架设计
		基于Spring-Cloud的微服务框架设计 先进行大的整体的框架整理,然后在针对每一项进行具体的详细介绍 
- SpringCloud学习--微服务架构
		目录 微服务架构快速指南 SOA Dubbo Spring Cloud Dubbo与SpringCloud对比 微服务(Microservice)架构快速指南 什么是软件架构? 软件架构是一个包含各种 ... 
- springCloud搭建微服务集群+Zuul服务器端负载均衡
		概述 最近研究了一下springCloud的微服务集群,主要用到了SpringCloud的服务发现和服务器端负载均衡,所有的项目都是用的springboot,可以和springCloud无缝对接. 技 ... 
- SpringCloud与微服务系列专栏
		一. 前置知识 学习SpringCloud之前需要具备和掌握如下框架和工具的使用:SpringMVC,Spring,Spring Boot,Mybatis,Maven,Git. SpringCloud ... 
- springCloud进阶(微服务架构&Eureka)
		springCloud进阶(微服务架构&Eureka) 1. 微服务集群 1.1 为什么要集群 为了提供并发量,有时同一个服务提供者可以部署多个(商品服务).这个客户端在调用时要根据一定的负责 ... 
- 一个C#开发者学习SpringCloud搭建微服务的心路历程
		前言 Spring Cloud很火,很多文章都有介绍如何使用,但对于我这种初学者,我需要从创建项目开始学起,所以这些文章对于我的启蒙,帮助不大,所以只好自己写一篇文章,用于备忘. SpringClou ... 
- SpringCloud的微服务网关:zuul(理论)
		参考链接:https://springcloud.cc/spring-cloud-dalston.html 一.概念与定义 1.为什么要引入API网关 后期维护:路由规则和服务实例列表困难 系统架构: ... 
- springcloud 新增微服务
		个人记录 记录公司微服务项目,模块添加的步骤 一 创建Module 选择maven groupid和artifactid 参考 pom文件 <project xmlns="http: ... 
- 深入理解SpringCloud与微服务构建
		旭日Follow_24 的CSDN 博客 ,全文地址请点击: https://blog.csdn.net/xuri24/article/details/81742534 目录 一.SpringClou ... 
随机推荐
- Sublime Text怎样自定义配色和主题
			一.自定义配色方案 1 基础知识 配色方案[Color Scheme]文件保存在以下路径[ST安装目录]: "D:\Program Files\Sublime Text\Packages\C ... 
- Linux--网络基础(概念+协议的了解+OSI七层模型,TCP/IP五层协议,网络数据传输流程)
			网络的发展 网络的发展有下面几个阶段: 独立模式:计算机最开始是以单机模式被广泛使用的.每一台计算机都是独立的,之间不能够进行数据共享与通信 网络互联: 计算机之间可以链接在一起,完成数据共享,计算机 ... 
- 打地鼠(susliks) 方法记录
			[SDOI2011]打地鼠 题目描述 2020.4.29 数据更新. 打地鼠是这样的一个游戏:地面上有一些地鼠洞,地鼠们会不时从洞里探出头来很短时间后又缩回洞中.玩家的目标是在地鼠伸出头时,用锤子砸其 ... 
- 论文笔记 - Fantastically Ordered Prompts and Where to Find Them: Overcoming Few-Shot Prompt Order Sensitivity
			prompt 的影响因素 Motivation Prompt 中 Example 的排列顺序对模型性能有较大影响(即使已经校准参见好的情况下,选取不同的排列顺序依然会有很大的方差): 校准可以大幅度提 ... 
- JavaWeb2
			1. web服务器软件:Tomcat 1.1 概述 服务器:安装了服务器软件的计算机 服务器软件:接受用户的请求,处理请求,做出响应 web服务器软件:接受用户的请求,处理请求,做出响应 在web服务 ... 
- mybatis实现数据行级权限拦截
			最近在做一个测试平台,其中有一个需求是用户只能看到他有权限的项目数据.一开始这个需求只针对用例模块,我直接在sql后面加上了关联项目权限表.后面因为其他模块也需要这个权限判断,故打算把关联sql抽取出 ... 
- navisworks2021保姆级下载安装教程
			navisworks2021 WIN10 64位安装步骤:1.先使用"百度网盘客户端"下载NV_CN_2021软件安装包到电脑磁盘里,并解压缩,安装前先断网,然后找到Autodes ... 
- Thinkphp6使用腾讯云发送短信步骤
			1.前提条件国内短信地址:https://console.cloud.tencent.com/smsv2 已开通短信服务,具体操作请参见 国内短信快速入门.如需发送国内短信,需要先 购买国内短信套餐包 ... 
- MongoDB - 索引知识
			索引简介 什么是索引 索引最常用的比喻就是书籍的目录,查询索引就像查询一本书的目录. 索引支持 MongoDB 查询的高效执行.如果没有索引,MongoDB 必须扫描集合中每一个文档,以选择与查询语句 ... 
- linux内核源码下载地址
			一.官网链接 https://www.kernel.org/ 二.HTTP https://www.kernel.org/pub/ 三.GIT https://git.kernel.org/ 四.镜像 ... 
