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之间进行权衡

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与微服务的更多相关文章

  1. 用SpringCloud进行微服务架构演进

    在<架构师必须要知道的阿里的中台战略与微服务> 中已经阐明选择SpringCloud进行微服务架构实现中台战略,因此下面介绍SpringCloud的一些内容,SpringCloud已经出来 ...

  2. 基于Spring-Cloud的微服务框架设计

    基于Spring-Cloud的微服务框架设计 先进行大的整体的框架整理,然后在针对每一项进行具体的详细介绍

  3. SpringCloud学习--微服务架构

    目录 微服务架构快速指南 SOA Dubbo Spring Cloud Dubbo与SpringCloud对比 微服务(Microservice)架构快速指南 什么是软件架构? 软件架构是一个包含各种 ...

  4. springCloud搭建微服务集群+Zuul服务器端负载均衡

    概述 最近研究了一下springCloud的微服务集群,主要用到了SpringCloud的服务发现和服务器端负载均衡,所有的项目都是用的springboot,可以和springCloud无缝对接. 技 ...

  5. SpringCloud与微服务系列专栏

    一. 前置知识 学习SpringCloud之前需要具备和掌握如下框架和工具的使用:SpringMVC,Spring,Spring Boot,Mybatis,Maven,Git. SpringCloud ...

  6. springCloud进阶(微服务架构&Eureka)

    springCloud进阶(微服务架构&Eureka) 1. 微服务集群 1.1 为什么要集群 为了提供并发量,有时同一个服务提供者可以部署多个(商品服务).这个客户端在调用时要根据一定的负责 ...

  7. 一个C#开发者学习SpringCloud搭建微服务的心路历程

    前言 Spring Cloud很火,很多文章都有介绍如何使用,但对于我这种初学者,我需要从创建项目开始学起,所以这些文章对于我的启蒙,帮助不大,所以只好自己写一篇文章,用于备忘. SpringClou ...

  8. SpringCloud的微服务网关:zuul(理论)

    参考链接:https://springcloud.cc/spring-cloud-dalston.html 一.概念与定义 1.为什么要引入API网关 后期维护:路由规则和服务实例列表困难 系统架构: ...

  9. springcloud 新增微服务

    个人记录 记录公司微服务项目,模块添加的步骤 一  创建Module 选择maven groupid和artifactid 参考 pom文件 <project xmlns="http: ...

  10. 深入理解SpringCloud与微服务构建

    旭日Follow_24 的CSDN 博客 ,全文地址请点击: https://blog.csdn.net/xuri24/article/details/81742534 目录 一.SpringClou ...

随机推荐

  1. Spring Cloud 整合 nacos 实现动态配置中心

    上一篇文章讲解了Spring Cloud 整合 nacos 实现服务注册与发现,nacos除了有服务注册与发现的功能,还有提供动态配置服务的功能.本文主要讲解Spring Cloud 整合nacos实 ...

  2. [Pyhton] SimPy 离散事件模拟框架详解 —— 以一个简单的汽车充电排队模拟为例

    目录 一.背景知识 二.SimPy 讲解 2.1 SimPy 概述 2.2 基本概念 2.3 一个汽车开开停停的例子 2.4 在走走停停过程中增加充电过程(过程交互) 2.5 共享资源 三.后续 参考 ...

  3. Codeforces 1670 E. Hemose on the Tree

    题意 给你个数p,n = 2^p: 有一棵树有n个节点,告诉你怎么连边: 每个点有个权值,每条边也有个权值,权值需要自行分配,[1,2,3..n...2n-1],总共2n-1个权值: 你需要选一个节点 ...

  4. 又拍云之 Keepalived 高可用部署

    在聊 Keepalived 之前,我们需要先简单了解一下 VRRP.VRRP(Virtual Router Redundancy Protocol)即虚拟路由冗余协议,是专门为了解决静态路由的高可用而 ...

  5. 洛谷P4135 Ynoi2016 掉进兔子洞 (带权bitset?/bitset优化莫队 模板) 题解

    题面. 看到这道题,我第一反应就是莫队. 我甚至也猜出了把所有询问的三个区间压到一起处理然后分别计算对应询问答案. 但是,这么复杂的贡献用什么东西存?难道要开一个数组 query_appear_tim ...

  6. 如何使用C#在Excel中插入分页符

    在日常办公中,我们经常会用到Excel文档来帮助我们整理数据.为了方便打印Excel工作表,我们可以在Excel中插入分页符.各种处理软件一般都会自动按照用户所设置页面的大小自动进行分页,以美化文档的 ...

  7. Leetcode-SQL学习计划-SQL入门-584:寻找用户推荐人

    建表语句: Create table If Not Exists Customer (id int, name varchar(25), referee_id int) Truncate table ...

  8. k8s-学习笔记总结(从入门到放弃的学习路线)

    刚入门学习k8s,我觉得挺难的,一头雾水,买了一本<Kubernetes权威指南>,真的很厚.我觉得作为应用开发人员的学习路线,不要想着一口气看完k8s的所有概念,要逐步学习,要看完这么厚 ...

  9. dotnet new cli 以及Abp-cli命令的简单使用

    1:要求 首先dotnet new  需要 .NET Core 3.1 SDK 以及更高版本 dotnet new - 根据指定的模板,创建新的项目.配置文件或解决方案 2:变化 从 .NET 7 S ...

  10. centos7安装MySql8.0.29教程

    个人名片: 对人间的热爱与歌颂,可抵岁月冗长 Github‍:念舒_C.ying CSDN主页️:念舒_C.ying 个人博客 :念舒_C.ying @ 目录 1 安装之前检测系统是否有自带的MySQ ...