栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringCloud 微服务(上)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringCloud 微服务(上)

SpringCloud 微服务
  • 基础构建与回顾
    • 前提准备
      • Boot和Cloud版本选型
      • Cloud组件停更说明
    • 父工程创建
      • DependencyManagement和Dependencies
    • 支付模块构建
      • 建module、改POM、写YML、主启动
      • 基础业务编写
    • 消费者订单模块
    • 工程重构
  • Eureka
    • 基础知识
    • EurekaServer单机服务端安装
    • 服务8001入驻进eurekaServer
    • Eureka集群原理说明
    • Eureka集群环境构建
    • 订单支付两微服务注册进Eureka集群
    • 支付微服务集群配置
    • actuator微服务信息完善
    • 服务发现Discovery
    • Eureka自我保护理论知识
    • 怎么禁止自我保护
      • 注册中心eurekaServer端7001
      • 生产者客户端eurekaClient端8001
  • zookeeper
  • CAP理论
  • Ribbon
    • Ribbon入门介绍
    • Ribbon的负载均衡和Rest调用
    • Ribbon默认自带的负载规则
    • Ribbon默认负载轮训算法原理
      • 轮询 RoundRobinRule源码分析
      • Ribbon之手写轮询算法
  • OpenFeign
    • 概述
    • OpenFeign服务调用
    • OpenFeign超时控制
  • END


主要学习

基础构建与回顾 前提准备 Boot和Cloud版本选型

截止2021年9月官网推荐

详细版本对应表,需严格按照此表来,格式化后

本次学习选择版本,严格要求

项目版本
spring-cloudHoxton.SR1
spring-boot2.2.2.RELEASE
cloud-alibaba2.1.0RELEASE
JavaJava8
Maven3.5以上
MySQL5.7以上
Cloud组件停更说明

红框为重点学习

父工程创建

new Project 一个Maven工程




    4.0.0

    com.atguigu.springcloud
    cloud2020
    1.0-SNAPSHOT
    pom
    
    
    
        UTF-8
        1.8
        1.8
        4.12
        1.2.17
        1.18.0
        5.1.47
        1.1.16
        1.3.2
    

    
    
        
            
            
                org.springframework.boot
                spring-boot-dependencies
                2.2.2.RELEASE
                pom
                import
            
            
            
                org.springframework.cloud
                spring-cloud-dependencies
                Hoxton.SR1
                pom
                import
            
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                2.1.0.RELEASE
                pom
                import
            
            
                mysql
                mysql-connector-java
                ${mysql.version}
            
            
                com.alibaba
                druid
                ${druid.version}
            
            
                org.mybatis.spring.boot
                mybatis-spring-boot-starter
                ${mybatis.spring.boot.version}
            
            
                log4j
                log4j
                ${log4j.version}
            
            
                junit
                junit
                ${junit.version}
            
            
                org.projectlombok
                lombok
                ${lombok.version}
                true
            
        
    


DependencyManagement和Dependencies


这样做的好处就是: 如果有多个子项目都引用同一样的依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样想升级或切换到另一个版本时,只需在顶层父容器里更新,而不需要一个一个子项目的修改l;另外如果某个子项目需要另外的一个版本,只需声明version版本

dependencyManagement里只是声明依赖,并不引入实现,因此子项目需要显示的声明需要用的依赖

支付模块构建 建module、改POM、写YML、主启动

new Module 一个Maven工程


    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-actuator
    
    
        org.mybatis.spring.boot
        mybatis-spring-boot-starter
    
    
        com.alibaba
        druid-spring-boot-starter
        1.1.10
    
    
        mysql
        mysql-connector-java
    
    
        org.springframework.boot
        spring-boot-starter-jdbc
    
    
        org.springframework.boot
        spring-boot-starter-test
        test
    


server:
  port: 8001

spring:
  application:
    name: cloud-payment-service
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://000.000.000.000:3306/db2020_cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities
@SpringBootApplication
public class PaymentMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentMain8001.class,args);
    }
}
基础业务编写

@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommonResult {

    private Integer code;
    private String message;
    private T data;

    public CommonResult(Integer code, String message) {
        this(code,message,null);
    }
}
消费者订单模块

利用RestTemplate发起HTTP远程调用,最简单的远程调用

@Configuration
public class ApplicationContextConfig {
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
@RestController
public class OrderController {

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

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/consumer/payment/create")
    public CommonResult create(Payment payment){

        return restTemplate.postForObject(PAYMENT_URI+"/payment/create",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/{id}")
    public CommonResult getById(@PathVariable("id") Long id){

        return restTemplate.getForObject(PAYMENT_URI+"/payment/"+id,CommonResult.class);
    }
}
工程重构

系统中有重复部分,重构

新建module cloud-api-common放公共类


    
        org.projectlombok
        lombok
        true
    
    
        cn.hutool
        hutool-all
        5.1.0
    

抽离其他module中相同的类


被抽取的module加入依赖,并删除被抽离的类和包,公共module clean、install


    com.atguigu.springcloud
    cloud-api-common
    ${project.version}

Eureka 基础知识

什么是服务治理
管理服务与服务之间的依赖,实现服务调用、负载均衡、服务注册与发现、容错等等。

什么是服务注册与发现

当服务启动时,会把自己的服务信息,如服务通信地址等以别名的方式注册到注册中心。另一方(消费者、服务提供者)以改别名的方式去注册中心上获取实际的服务通讯地址,然后再实现本地RPC调用

Eureka的两个组件 EurekaServer和EurekaClient
EurekaServer:提供服务注册
各个微服务节点配置启动后,会在EurekaServer中进行

EurekaServer单机服务端安装

new一个Module:cloud-eureka-server7001


    
    
         org.springframework.cloud 
        spring-cloud-starter-netflix-eureka-server
    
    
         com.atguigu.springcloud 
         cloud-api-common 
         ${project.version} 
    
    
         org.springframework.boot 
         spring-boot-starter-web 
    
    
         org.springframework.boot 
         spring-boot-starter-actuator 
    
    
    
         org.projectlombok 
         lombok 
         true 
    
    
         org.springframework.boot 
         spring-boot-starter-test 
         test 
    

@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}
server:
  port: 7001

spring:
  application:
    name: cloud-eureka-server7001

eureka:
  instance:
    hostname: localhost
  client:
    fetch-registry: false  #不注册自己
    register-with-eureka: false # 不检索自己
    service-url:
      #      defaultZone: http://www.eureka7002.com:7002/eureka/
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

服务8001入驻进eurekaServer

    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-client

eureka:
  client:
    # 表示是否酱紫注册进eurekaServer 默认为true
    fetch-registry: true
    # 是否从EurekaServer抓去已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka

启动类 @EnableEurekaClient

服务80同上

Eureka集群原理说明


每个eureka互相注册,相互守望

Eureka集群环境构建

再创建一个cloud-eureka-server7002 同 cloud-eureka-server7001一样
注册地址defaultZone为对方

server:
  port: 7002

spring:
  application:
    name: cloud-eureka-server7002

eureka:
  instance:
    hostname: eureka7002.com
  client:
    fetch-registry: false  #不注册自己
    register-with-eureka: false # 不检索自己
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
订单支付两微服务注册进Eureka集群

只需将Eureka集群的两个地址填入

defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
支付微服务集群配置

再new Module 一个 cloud-provider-payment8002
注意application.name与8001保持一致

server:
  port: 8002

spring:
  application:
    name: cloud-payment-service

调用方,调用地址必须为注册中心中服务的别名,且RestTemplate必须加上@LoadBalanced注解,完成对服务的轮询调用

private final static String PAYMENT_URI = "http://CLOUD-PAYMENT-SERVICE";

------------

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
    return new RestTemplate();
}
actuator微服务信息完善

主机名称:服务名称修改
访问信息有IP信息提示

eureka:
  instance:
    instance-id: payment8002 
    prefer-ip-address: true #访问路径可以显示IP地址

服务发现Discovery

对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

启动类@EnableDiscoveryClient开启服务发现

@Resource
private DiscoveryClient discoveryClient;

@GetMapping("/discovery")
public Object discovery() {
    List services = discoveryClient.getServices();
    services.forEach(service->{
        System.out.println("----service"+service);
    });
    List instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
    for (ServiceInstance instance : instances) {
        System.out.println(instance.getServiceId()+"t" + instance.getHost()+"t"+ instance.getPort()+"t"+instance.getUri());;
    }

    return this.discoveryClient;
}

Eureka自我保护理论知识





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

属于CAP里面的AP分支

  • C(一致性):所有的节点上的数据时刻保持同步
  • A(可用性):每个请求都能接受到一个响应,无论响应成功或失败
  • P(分区容错):系统应该能持续提供服务,即使系统内部有消息丢失(分区)
怎么禁止自我保护 注册中心eurekaServer端7001

出产默认,自我保护机制是开启的
eureka.server.enable-self-preservation=true

使用eureka.server.enable-self-preservation=false 可以禁用自我保护模式

生产者客户端eurekaClient端8001

eureka.instance.lease-renewal-interval-in-seconds=30
Eureka 客户端向服务端发送心跳的时间间隔 , 单位为秒 ( 默认是 30 秒 )

eureka.instance.lease-expiration-duration-in-seconds=90
Eureka 服务端在收到最后一次心跳后等待时间上限 , 单位为秒 ( 默认是 90 秒 ), 超时剔除服务

zookeeper CAP理论




AP:Eureka

CP:Zookeeper、Consul


Ribbon Ribbon入门介绍

是什么

Ribbon目前也进入维护模式:未来替换方案 – SpringCloud loadBalancer



前面我们讲解过了80通过轮询负载访问8001/8002

总结:负载均衡+RestTemplate调用

Ribbon的负载均衡和Rest调用


Ribbon其实就是一个软负载均衡的客户端组件, 他可以和其他所需请求的客户端结合使用,和eureka结合只是其中一个实例.

Eureka依赖中自带整合了Ribbon


getForEntity能否获取到响应体的完整信息, getForOject只获取传输对象的json串

@GetMapping("/consumer/payment/getforentity/{id}")
public CommonResult getById2(@PathVariable("id") Long id){
    ResponseEntity entity = restTemplate.getForEntity(PAYMENT_URI + "/payment/" + id, CommonResult.class);
    if(entity.getStatusCode().is2xxSuccessful()){
        return entity.getBody();
    }
    return new CommonResult<>(400,"操作失败");
}

@GetMapping("/consumer/payment/createforentity")
public CommonResult create2(Payment payment){
    ResponseEntity entity = restTemplate.postForEntity(PAYMENT_URI + "/payment/create", payment, CommonResult.class);
    if(entity.getStatusCode().is2xxSuccessful()){
        return entity.getBody();
    }
    return new CommonResult<>(400,"操作失败");
}
Ribbon默认自带的负载规则

IRule:根据特定算法从服务列表中选取一个要访问的服务

负载规则替换

1、不能和主启动类在同一包下

@Configuration
public class MySelfRule {

    @Bean
    public IRule myRule(){
        return new RandomRule();//定义为随机
    }
}

启动类添加注解

@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
Ribbon默认负载轮训算法原理

轮询 RoundRobinRule源码分析


choose方法中做服务器选择的具体实现

JUC、CAS、自旋锁

Ribbon之手写轮询算法

手写一个负载的算法
原理+JUC(CAS+自旋锁的复习)

接口及其实现

public interface LoadBalancer {
    ServiceInstance instances(List serviceInstances);
}

@Component
public class MyLB implements LoadBalancer {

    private AtomicInteger atomicInteger = new AtomicInteger(0);

    public final int getAndIncrement() {
        int current;
        int next;
        do {
            current = this.atomicInteger.get();
            next = current >= 2147483647 ? 0 : current + 1;
        } while (!this.atomicInteger.compareAndSet(current, next));
        System.out.println("****第几次访问,次数next:" + next);
        return next;
    }


    @Override
    public ServiceInstance instances(List serviceInstances) {
        int index = getAndIncrement() % serviceInstances.size();
        return serviceInstances.get(index);
    }
}

调用

@Resource
private LoadBalancer loadBalancer;

@Resource
private DiscoveryClient discoveryClient;

@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB(){
    List instances = discoveryClient.getInstances("cloud-payment-service");
    if (instances == null || instances.size()<=0){
        return null;
    }
    ServiceInstance serviceInstance = loadBalancer.instances(instances);
    URI uri = serviceInstance.getUri();
    return restTemplate.getForObject(uri+"/payment/lb",String.class);
}
OpenFeign 概述

Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需 创建一个接口并在接口上添加注解即可

OpenFeign服务调用

1、微服务调用接口+@FeignClient
2、用在消费端,创建一个消费端
关键依赖


    org.springframework.cloud
    spring-cloud-starter-openfeign


    org.springframework.cloud
    spring-cloud-starter-netflix-eureka-client

3、YML配置注册地址

server:
  port: 80

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/
    register-with-eureka: false

4、主启动类添加注解@EnableFeignClients

5、业务类,分装在在接口,控制层通过接口调用

@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {

    @GetMapping("/payment/{id}")
    CommonResult getPaymentById(@PathVariable("id") Long id);
}

---------------

@RestController
public class PaymentController {
    @Autowired
    PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/{id}")
    public CommonResult getPaymentById(@PathVariable("id") Long id){
        return paymentFeignService.getPaymentById(id);
    }
}

OpenFeign超时控制

#设置feign客户端超时时间
ribbon:
  ReadTimeout: 5000
  ConnectTimeout: 5000

日志增强

@Configuration
public class FeignConfig {

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

logging:
  level:
    com.atguigu.springcloud.service.PaymentFeignService: debug
END
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/292826.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号