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. Android10 dex2oat实践

    最近看到一篇博客:Android性能优化之Android 10+ dex2oat实践,对这个优化很感兴趣,打算研究研究能否接入到项目中.不过该博客只讲述了思路,没有给完整源码.本项目参考该博客的思路, ...

  2. Dive into TensorFlow系列(1)-静态图运行原理

    接触过TensorFlow v1的朋友都知道,训练一个TF模型有三个步骤:定义输入和模型结构,创建tf.Session实例sess,执行sess.run()启动训练.不管是因为历史遗留代码或是团队保守 ...

  3. GAC简述

    GAC简介 GAC全称是Global Assembly Cache作用是可以存放一些有很多程序都要用到的公共Assembly,例如System.Data.System.Windows.Forms等等. ...

  4. Go语言核心36讲19

    你好,我是郝林,今天我们继续分享go语句执行规则的内容. 在上一篇文章中,我们讲到了goroutine在操作系统的并发编程体系,以及在Go语言并发编程模型中的地位和作用等一系列内容,今天我们继续来聊一 ...

  5. 用最少的代码模拟gRPC四种消息交换模式

    我们知道,建立在HTTP2/3之上的gRPC具有四种基本的通信模式或者消息交换模式(MEP: Message Exchange Pattern),即Unary.Server Stream.Client ...

  6. 树莓派配置uwsgi服务

    前言 我配置 uwsgi 服务是为了运行给 python flask 项目,如果直接 pip3 install uwsgi 得到的uwsgi服务可以直接使用,只不过需要在命令行中启动服务(当然也可以使 ...

  7. c++学习笔记(入门)

    1 struct和class的区别 struct成员变量(成员函数)的访问属性缺省的情况下默认为public. class成员变量(成员函数)的访问属性缺省的情况下默认为private. 2 初始化列 ...

  8. Go1.20 新版覆盖率方案解读

    玩过Go覆盖率的同学当有所了解,Go的覆盖率方案最初的设计目标仅是针对单测场景,导致其局限性很大.而为了适配更多的场景,行业内各种博客.插件.黑科技介绍也层出不穷.当然,过去我们也开源过Go系统测试覆 ...

  9. GP之gpbackup备份

    从GP6.0后,使用gpbackup命令来实现备份.但GP里是不自带的,需要自己重新下载并编译和安装. 一.安装 (1)master上go下载并配置profile环境变量 go下载地址 :https: ...

  10. STM32与PS2的无线通信和相关函数介绍

    PS2采用SPI通信协议 源码和参考文件获取:https://github.com/Sound-Sleep/PS2_Based_On_STM32 接收器接口 DI:手柄->主机,时钟的下降沿传送 ...