三万字图文归纳整理分布式系统微服务(二)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 三万字图文归纳整理分布式系统微服务(二)

三、Dubbo

1. 如果注册中心集群都挂掉,发布者和订阅者之间还能通信么?

# 能,因为有缓存;注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯 新的服务将无法发现;服务下架后仍旧有被调用的可能;

2. dubbo连接注册中心和直连的区别

连接注册中心:消费者动态发现服务提供者,实现软负载均衡和集群容错; 直连:不能动态增减提供者;开发、测试环境可通过指定Url方式绕过注册中心直连指定的服务地址,快速测试;

3. dubbo服务的容错机制

# http://dubbo.io/books/dubbo‐user‐book/demos/fault‐tolerent‐strategy.html Failover Cluster:失败自动切换,当出现失败,重试其它服务器,默认两次;Failfast Cluster:失败立即报错 Failsafe Cluster:失败安全,出现异常时,直接忽略 Failback Cluster:失败自动恢复,后台记录失败请求,定时重发 Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源 Broadcast Cluster:播调用所有提供者,逐个调用,任意一台报错则报错 # 要注意分布式事务

3.1 Failover Cluster

失败自动切换,缺省值,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。配置如下:生产者:

<dubbo:service retries="2" />

消费者:

<dubbo:reference retries="2" />

消费者具体到调用生产者的哪个方法:

<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>

3.2 Failfast Cluster

快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录 。配置如下:生产者:

<dubbo:service cluster="failfast" />

消费者:

<dubbo:reference cluster="failfast" />

3.3 Failsafe Cluster

失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作 。配置如下:生产者:

<dubbo:service cluster="failsafe" />

消费者:

<dubbo:reference cluster="failsafe" />

3.4 Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作 。配置如下:生产者:

<dubbo:service cluster="failback" />

消费者:

<dubbo:reference cluster="failback" />

3.5 Forking Cluster

并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作 ,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。配置如下:生产者:

<dubbo:service cluster=“forking" />

消费者:

<dubbo:reference cluster=“forking" />

3.6 Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错 。通常用于通知所有提供者更新缓存或日志等本地资源信息 。配置如下:生产者:

<dubbo:service cluster="broadcast" />

消费者:

<dubbo:reference cluster="broadcast" />

4. 具体问题示例1:关于服务调用超时

# 超时是针对消费端还是服务端?

dubbo的超时是争对客户端的,由于是一种NIO模式,消费端发起请求后得到一个ResponseFuture,然后消费端一直轮询这个ResponseFuture直至超时或者收到服务端的返回结果。虽然超时了,但仅仅是消费端不再等待服务端的反馈并不代表此时服务端也停止了执行。

# 超时在哪设置?

消费端

  • 全局控制
<dubbo:consumer timeout="1000"></dubbo:consumer>
  • 接口控制
  • 方法控制

服务端

  • 全局控制
<dubbo:provider timeout="1000"></dubbo:provider>
  • 接口控制
  • 方法控制

可以看到dubbo针对超时做了比较精细化的支持,无论是消费端还是服务端,无论是接口级别还是方法级别都有支持。

# 优先级是什么?

优先顺序为:客户端方法级>服务端方法级>客户端接口级>服务端接口级>客户端全局>服务端全局

# 实现原理消费端发起远程请求后,线程不会阻塞等待服务端的返回,而是马上得到一个ResponseFuture(ReponseFuture的实现类:DefaultFuture),消费端通过不断的轮询机制判断结果是否有返回。因为是通过轮询,轮询有个需要特别注要的就是避免死循环,所以为了解决这个问题就引入了超时机制,只在一定时间范围内做轮询,如果超时时间就返回超时异常

# 设置超时主要是解决什么问题?如果没有超时机制会怎么样?

对调用的服务设置超时时间,是为了避免因为某种原因导致线程被长时间占用,最终出现线程池用完返回拒绝服务的异常。

5. 服务提供者能实现失效踢出是什么原理

服务失效踢出基于zookeeper的临时节点原理。

临时节点的生命周期和客户端会话绑定,也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉

源码参考 RegistryService, 文档参考https://www.jianshu.com/p/f42c69e4bd3e?fromApp=1

# redis ‐ 脏数据由监控中心删除 # zookeeper ‐ 的临时节点

6. dubbo通讯协议

# 什么是通讯协议客户端服务端之间进行数据流转的一种约定,将数据按照标准格式来组装;# 协议内容头部信息(调用控制协议16B) + body(序列化) head = 魔数(2) + 标识(1) + 响应状态(1) + 消息ID(8) + 内容长度(4) body = dubbo版本 + 服务接口路径 + 服务版本号 + 服务方法名 + 参数描述符 + 参数值序列化 + 隐式参数

7. 大概率的问题

# dubbo文档,仔细阅读一遍,面试官可能随便抽取一个。

7.1 dubbo配置的方式和属性

dubbo的官网入门使用demo

1. 服务提供者

1.1 定义服务接口DemoService.java

package com.alibaba.dubbo.demo;
public interface DemoService {
    String sayHello(String name);
}

1.2 在服务提供方实现接口DemoServiceImpl.java

package com.alibaba.dubbo.demo.provider;
import com.alibaba.dubbo.demo.DemoService;
public class DemoServiceImpl implements DemoService {
    public String sayHello(String name) {
        return "Hello " + name;
    }
}

1.3 用Spring配置声明暴露服务provider.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="hello-world-app"  />
    <!-- 使用multicast广播注册中心暴露服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20880" />
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" />
    <!-- 和本地bean一样实现服务 -->
    <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>

1.4 加载Spring配置Provider.java

import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Provider {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/provider.xml"});
        context.start();
        System.in.read(); // 按任意键退出
    }
}

2. 服务消费者

2.1 通过Spring配置引用远程服务consumer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
    xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="consumer-of-helloworld-app"  />
    <!-- 使用multicast广播注册中心暴露发现服务地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />
    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="com.alibaba.dubbo.demo.DemoService" />
</beans>

2.2 加载Spring配置,并调用远程服务Consumer.java

import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.dubbo.demo.DemoService;
public class Consumer {
    public static void main(String[] args) throws Exception {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"http://10.20.160.198/wiki/display/dubbo/consumer.xml"});
        context.start();
        DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
        String hello = demoService.sayHello("world"); // 执行远程方法
        System.out.println( hello ); // 显示调用结果
    }
}

3. 总结

3.1 该接口需单独打包,在服务提供方和消费方共享 3.2 对服务消费方隐藏实现 3.3 也可以使用IoC注入

参考文章:https://www.cnblogs.com/leeSmall/p/9824591.html

7.2 dubbo底层协议

底层的调用协议http、rmi、netty(dubbo)7.3 dubbo服务注册与发现流程

微信图片_20220908142232.png

调用关系说明:

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败, 再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

总结:

生产者启动时把接口注册到注册中心,消费者启动时从注册中心获取生产者的接口列表放到本地,当有请求过来时,从消费者的本地获取生产者的接口列表选择一个生产者使用底层的调用协议(http、rmi、netty(dubbo))去调用,dubbo同时还能对服务进行监控

四、SpringBoot

1、SpringBoot与SpringMVC差异化在哪

1.首先这两者专注的领域不同,解决的问题也不一样 2.Spring MVC是基于 Servlet 的一个 MVC 框架,通过Dispatcher Servlet, ModelAndView解决 WEB 开发的问题.但是它的配置繁琐,大量 .xml、.properties文件。并且用maven管理项目的时候很容易出现jar包冲突。3.而Spring Boot 是基于Spring的一套快速开发整合包,它不仅仅包含了spring mvc ,还包括 spring JPA、spring security等实现自动配置,降低项目搭建的复杂度.而且自带tomcat.pom.xml 中可以使用 starter的方式配置,简化配置还能解决jar包冲 突问题。spring-boot-starter-web spring-boot-starter-data-jpa spring-boot-starter-redis 4.但他们的基础都是Spring 的 IOC 和 AOP 5.spring mvc 打包后是war包,spring boot 是jar包

2、为什么说Spring是一个容器

Spring通过IOC创建和管理bean实例,所以Spring也称为IOC容器

3、常用注解@Required @Autowired @Qualifier @Controller @RequestMapping

@Required注解作用于Bean setter方法上,用于检查一个Bean的属性的值在配置期间是否被赋予或设置(populated),否则,容器会抛出一个BeanInitializationException异常。

示例:

package com.jsoft.testspring.testannotationrequired;import org.springframework.beans.factory.annotation.Required;public class Student {    private Integer age;    private String name;    
    @Required    public void setAge(Integer age){        this.age = age;
    }    
    public Integer getAge(){        return this.age;
    }    
    public void setName(String name){        this.name = name;
    }    
    public String getName(){        return this.name;
    }
}

beans.xml:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd">
   <context:annotation-config/>
   <bean id="student" class="com.jsoft.testspring.testannotationrequired.Student">
        <property name="name" value="Jim"/>
   </bean></beans>

App.java:

package com.jsoft.testspring.testannotationrequired;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;/**
 * Hello world!
 * */public class App 
{    public static void main( String[] args )
    {
           ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
           Student student = (Student)applicationContext.getBean("student");
           System.out.println(student.getAge());
           System.out.println(student.getName());
    }
}

而此时的运行结果是出现了错误的,因为age的setter方法没有在bean中注入,而age的setter方法标记了@Required,也就是必须要输入,抛出的异常为:BeanInitializationException。

微信图片_20220908142240.png

那么我们将beans.xml补全之后:

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd">
   <context:annotation-config/>
   <bean id="student" class="com.jsoft.testspring.testannotationrequired.Student">
        <property name="name" value="Jim"/>
        <property name="age" value="27"/>
   </bean></beans>

此时的运行结果一切正常:

微信图片_20220908142326.png

@Autowired自动装配

@Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。

@Autowired默认按类型匹配的方式,在容器查找匹配的Bean,当有且仅有一个匹配的Bean 时,Spring将其注入@Autowired标注的变量中。

@Qualifier(指定注入Bean的名称)

如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称

使用 @Controller 定义一个 Controller 控制器

例1:

@Controllerpublic class MyController {
    @RequestMapping ( "/showView" )    public ModelAndView showView() {
       ModelAndView modelAndView = new ModelAndView();
       modelAndView.setViewName( "viewName" );
       modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " );       return modelAndView;
    }
}

例1所示@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器,这个接下来就会讲到。

单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要我们把这个控制器类交给Spring 来管理。

这个时候有两种方式可以把MyController 交给Spring 管理,好让它能够识别我们标记的@Controller 。

第一种方式是在SpringMVC 的配置文件中定义MyController 的bean 对象。

第二种方式是在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。

< context:component-scan base-package = "com.host.app.web.controller" >
       < context:exclude-filter type = "annotation"
           expression = "org.springframework.stereotype.Service" />
    </ context:component-scan >

注:上面 context:exclude-filter 标注的是不扫描 @Service 标注的类

使用 @RequestMapping 来映射 Request 请求与处理器

例1可以使用@RequestMapping 来映射URL 到控制器类,或者是到Controller 控制器的处理方法上。当@RequestMapping 标记在Controller 类上的时候,里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的;当Controller 类上没有标记@RequestMapping 注解时,方法上的@RequestMapping 都是绝对路径。这种绝对路径和相对路径所组合成的最终路径都是相对于根路径“/ ”而言的。

在这个控制器中,因为MyController 没有被@RequestMapping 标记,所以当需要访问到里面使用了@RequestMapping 标记的showView 方法时,就是使用的绝对路径/showView.do 请求就可以了。

例2:

@Controller
@RequestMapping ( "/test" )public class MyController {
    @RequestMapping ( "/showView" )    public ModelAndView showView() {
       ModelAndView modelAndView = new ModelAndView();
       modelAndView.setViewName( "viewName" );
       modelAndView.addObject( " 需要放到 model 中的属性名称 " , " 对应的属性值,它是一个对象 " );       return modelAndView;
    }
}
相关文章
|
3月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
3月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
2月前
|
消息中间件 存储 负载均衡
微服务与分布式系统设计看这篇就够了!
【10月更文挑战第12天】 在现代软件架构中,微服务和分布式系统设计已经成为构建可扩展、灵活和可靠应用程序的主流方法。本文将深入探讨微服务架构的核心概念、设计原则和挑战,并提供一些关于如何在分布式系统中实现微服务的实用指导。
67 2
|
4月前
|
监控 Java 开发者
随着软件开发的发展,传统单体应用已难以适应现代业务需求,微服务架构因此兴起,成为构建可伸缩、分布式系统的主流
随着软件开发的发展,传统单体应用已难以适应现代业务需求,微服务架构因此兴起,成为构建可伸缩、分布式系统的主流。本文探讨Java微服务架构的设计原则与实践。核心思想是将应用拆分为独立服务单元,增强模块化与扩展性。Java开发者可利用Spring Boot等框架简化开发流程。设计时需遵循单一职责、自治性和面向接口编程的原则。以电商系统为例,将订单处理、商品管理和用户认证等拆分为独立服务,提高可维护性和容错能力。还需考虑服务间通信、数据一致性及监控等高级话题。掌握这些原则和工具,开发者能构建高效、可维护的微服务应用,更好地应对未来挑战。
89 1
|
4月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
4月前
|
Cloud Native 云计算 微服务
云原生时代:企业分布式应用架构的惊人蜕变,从SOA到微服务的大逃亡!
【8月更文挑战第8天】在云计算与容器技术推动下,企业分布式应用架构正经历从SOA到微服务再到云原生的深刻变革。SOA强调服务重用与组合,通过标准化接口实现服务解耦;微服务以细粒度划分服务,增强系统灵活性;云原生架构借助容器化与自动化技术简化部署与管理。每一步演进都为企业带来新的技术挑战与机遇。
128 6
|
4月前
|
存储 监控 安全
|
4月前
|
监控 负载均衡 Java
(九)漫谈分布式之微服务组件篇:探索分布式环境下各核心组件的必要性!
本文将深入探讨微服务中各个组件的必要性,以此帮助各位更好地加深对分布式系统的掌握度。
142 1
|
5月前
|
消息中间件 Java 开发者
Spring Cloud微服务框架:构建高可用、分布式系统的现代架构
Spring Cloud是一个开源的微服务框架,旨在帮助开发者快速构建在分布式系统环境中运行的服务。它提供了一系列工具,用于在分布式系统中配置、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等领域的支持。
192 5
|
2月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?