您的当前位置:首页正文

springboot第71集:字节跳动全栈一面经,一文让你走出微服务迷雾架构周刊

2024-11-27 来源:个人技术集锦

使用ThreadLocal来存储和管理每个线程的事务追踪对象,确保每个线程都有自己独立的事务上下文。方法首先尝试从ThreadLocal获取事务追踪对象,如果不存在,则尝试从数据库中查询。如果数据库中也不存在,则创建一个新的事务追踪对象,初始化其状态和时间信息,然后将其保存到数据库和ThreadLocal中。

这个错误消息表示 Vite 构建工具在尝试从你的 Vue 组件中导入 @toast-ui/chart/dist/toastui-chart.css 时遇到了问题,具体是因为无法解析该路径。这通常是因为以下几个原因之一:

如果在进行上述检查后仍然存在问题,你可以提供更多的构建配置信息或者项目结构细节,这样我可以提供更具体的帮助。

您遇到的错误信息表明您的 Elasticsearch 集群出现了问题。cluster_block_exception 错误,提示为 "blocked by: [SERVICE_UNAVAILABLE/1/state not recovered / initialized]",通常意味着集群尚未准备好处理查询。这种情况可能在启动过程中发生,当集群的某些状态部分还未初始化或完全恢复时。以下是一些诊断和解决此问题的步骤和建议:

诊断问题

  1. 检查集群健康状况:

    bash
    Copy code
    curl -X GET "localhost:9200/_cluster/health?pretty"
  • 绿色状态表示一切正常,黄色表示所有数据都可用但某些副本未被分配,红色表示某些数据由于某种原因无法使用。

  • 执行 Elasticsearch 集群健康 API 来检查您的集群状态。这将告诉您集群是处于绿色、黄色还是红色状态。

查看集群日志:

  • 检查 Elasticsearch 节点的日志。寻找可能指示集群健康状况不佳或恢复未完成的任何警告或错误信息。

检查节点状态:

bash
Copy code
curl -X GET "localhost:9200/_cat/nodes?v&pretty"
  • 确保所有节点都已启动并且彼此间可以通信。您可以使用 _cat/nodes API 查看节点列表及其状态。

检查磁盘空间:

  • 节点上的磁盘空间不足可能会引起问题。确保集群中所有节点都有足够的磁盘空间。

查看集群设置:

bash
Copy code
curl -X GET "localhost:9200/_cluster/settings?include_defaults=true&pretty"
  • 有时关于分片的设置,如 cluster.routing.allocation.enablecluster.blocks.read_only 可能会引起问题。检查当前设置:

解决问题

  1. 解决任何磁盘空间问题:

  • 如果磁盘空间不足,请清理空间或为节点增加更多存储。

确保所有节点都在线:

  • 如果有任何节点宕机,请将它们重新启动。确保节点间的网络连接正常。

给予集群时间恢复:

  • 如果集群正在恢复,请给它一些时间。大型集群可能需要一段时间来初始化。

必要时调整集群设置:

bash
Copy code
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
  "persistent": {
    "cluster.routing.allocation.enable": "all"
  }
}'
  • 如果设置过于限制性或配置错误(如只读设置),请相应调整它们:

重启 Elasticsearch 服务:

  • 如果上述步骤均无效,考虑在所有节点上完全重启 Elasticsearch 服务。有时全新的重启可以解决导致集群状态未初始化的问题。

解决问题后:

解决问题后,建议密切监控集群的性能和日志,确保问题不再发生,并且由于所做更改没有引发新的问题。

您遇到的 NullPointerException 通常表示尝试在 null 引用上进行操作。在您的代码中,这个异常发生在尝试将 depositInfo.getAmount()params.getFreezAmount() 转换为 BigDecimal 时。如果这两个方法中的任何一个返回 null,则在尝试创建 BigDecimal 实例时会抛出 NullPointerException

在 Nginx 中配置图片上传限制,主要涉及到调整客户端请求体的大小,这通常是通过 client_max_body_size 指令来控制的。默认情况下,Nginx 设置这个值为 1M(1兆字节),这意味着如果上传的文件超过了这个大小,服务器就会返回一个 413 错误(请求实体过大)。

如果你需要调整这个设置来允许更大的图片上传,你可以按照以下步骤进行配置:

  1. 打开 Nginx 配置文件:这个文件通常位于 /etc/nginx/nginx.conf 或者是某个特定站点的配置文件中,比如 /etc/nginx/sites-available/your_site_config

  2. 修改或添加 client_max_body_size 指令

  • 你可以在 http 块中设置这个值,这样会影响所有服务器:

http {
    client_max_body_size 100M;
}

也可以在特定的 serverlocation 块中设置,仅影响特定的虚拟主机或位置:

server {
    location /upload {
        client_max_body_size 100M;
    }
}

重启 Nginx 服务:配置更新后,需要重启 Nginx 服务来使更改生效。可以使用以下命令:

sudo systemctl restart nginx

  • 请求参数

1.1通用参数

1.1之中参数需要置于queryString之中

参数名称含义规则说明是否必填默认值
key客户唯一标示用户申请,由高德后台自动分配

1.2业务参数

1.2之中参数需要置于body体之中

Post数据格式为JsonArray,里面需要包含JsonObject

x、y、ag、tm、sp此为一个JsonObject,每个JsonObject需要保持完整,最多有500个JsonObject

参数名称含义规则说明是否必填默认值
x经度小数点后最多6位
y纬度小数点后最多6位
ag角度与正北方向的夹角,小数、整数均可需要注意的是,计算时需要参考到设备的角度来判别方向,如果ag参数是0,或者是不合法的参数,会比较大概率导致纠偏计算失败。
tm时间tm以秒为单位,第一个采集点的tm值从1970年0点开始,其他采集点为与第一个采集点时间的差值
sp速度车辆/设备的移动速度,单位:km/h ,小数、整数均可,在计算纠偏时如果速度值不合理,会比较大概率导致纠偏计算失败。在使用时请传入合理的速度值。

请注意:纠偏效果取决于轨迹中定位点的密集程度,定位点间隔效果最优理论值5~10秒,也就是每5秒或者10秒一个定位点构成的轨迹纠偏效果最优。

轨迹纠偏

https://restapi.amap.com/v4/grasproad/driving

轨迹纠偏服务插件。用于将一组带方向的、可能偏离道路的经纬度轨迹,纠正为准确沿着道路的一条经纬度路径。比如将一组间隔采集的车辆位置和朝向、速度、时间等信息传入,可以得到一组沿着道路行进的经纬度路径和总的行驶历程。

准备原始轨迹数据

纠偏之前需要按照下面的数据规格准备原始轨迹点,x、y、sp、ag、tm分别代表经度、纬度、速度、角度、时间。

需要注意:

  1. 经纬度应该是高德坐标,而不是GPS直接采集的坐标,可通过[AMap.convertFrom]转化

  2. tm以秒为单位,第一个采集点的tm值从1970年0点开始,其他采集点为与第一个采集点时间的差值

详细描述和用途

  1. **checkTableExist**:

  • 此标志可用于检查主表或主要表在应用程序数据库中的存在。在执行任何CRUD(创建、读取、更新、删除)操作之前,应用程序可能会检查此标志以确保表可用。

**checkBackupTableExist**:

  • 这个变量可能用于验证备份表的存在。备份表通常用于灾难恢复或存档目的。在执行备份操作或将数据迁移到备份表之前,应用程序会检查这个标志。

**checkLogTableExist**:

  • 这个标志可用于检查日志表的存在,这对于需要维护事务或操作记录以便审计和跟踪的应用程序至关重要。在记录任何数据之前,应用程序将通过检查这个标志来确保日志表存在。

  • 主键组成

    • 分区键(accountId, day) 的组合形成复合分区键。这意味着数据根据这两个字段被分区并分布在集群中。这对于按账户和日期查询日志非常有用,可以有效地检索特定账户在特定日子的所有日志。

    • 聚簇列logTimelogId 是聚簇列。在每个分区(accountIdday 的唯一组合)内,行首先按 logTime 排序,然后按 logId 排序。这支持在一天内有效查询日志,允许基于时间的日志检索或在已知ID的情况下检索特定日志。

  • 用途:特别适用于需要审计日志或详细日志的系统,这些日志条目频繁且需要基于时间和ID条件进行检索。

var originPath= [
        {"x":116.478928,"y":39.997761,"sp":19,"ag":0, "tm":1478031031},
        {"x":116.478907,"y":39.998422,"sp":10,"ag":0, "tm":2},
        {"x":116.479384,"y":39.998546,"sp":10,"ag":110,"tm":3},
        {"x":116.481053,"y":39.998204,"sp":10,"ag":120,"tm":4},
        {"x":116.481793,"y":39.997868,"sp":10,"ag":120,"tm":5},
        {"x":116.482898,"y":39.998217,"sp":10,"ag":30, "tm":6},
        {"x":116.483789,"y":39.999063,"sp":10,"ag":30, "tm":7},
        {"x":116.484674,"y":39.999844,"sp":10,"ag":30, "tm":8}]

功能说明

  1. client() 方法

  • 这个方法通过 @Bean 注解定义了一个Spring管理的Bean,名为 esRestHighLevelClient。这使得其他组件可以通过Spring的依赖注入使用这个Elasticsearch客户端。

  • 返回的是已经存在的 restHighLevelClient 实例,这保证了整个应用中使用的是同一个Elasticsearch连接实例。

destroy() 方法

  • 使用 @PreDestroy 注解,这保证了在Spring容器销毁Bean或关闭应用时,这个方法会被自动调用。

  • 方法内部检查了 restHighLevelClient 是否为 null,如果不为 null 则尝试关闭这个客户端,释放与其相关的资源。

  • 如果在关闭过程中发生了 IOException 异常,则会捕获这个异常并记录详细的错误信息。这是重要的,因为它可以帮助开发人员理解关闭失败的原因,及时进行问题诊断和修复。

BulkProcessor 配置详细说明

  • setBulkActions(10000) :达到 10,000 条请求时自动提交批量操作。

  • setBulkSize(new ByteSizeValue(8L, ByteSizeUnit.MB)) :批量数据大小达到 8MB 时自动提交。

  • setFlushInterval(TimeValue.timeValueSeconds(10)) :无论批次大小如何,每10秒自动提交一次批次。

  • setConcurrentRequests(8) :允许最多 8 个并发请求。

  • **setBackoffPolicy**:设置重试策略为固定退避,最多重试 3 次,每次间隔 1 秒。

  1. **RestHighLevelClient**:

  • 这是通过高级REST客户端与Elasticsearch进行交互的主入口。它提供了一种与Elasticsearch集群通信并对数据执行索引、搜索、更新和删除操作的直接方式。

  • 静态使用:将RestHighLevelClient设置为静态的通常是因为想在应用程序的各个部分之间共享单个实例,这是由于其线程安全的性质和创建多个客户端所带来的开销。

**BulkProcessor**:

  • BulkProcessor简化了批量索引和更新文档的过程。它设计用来吸收大量的索引请求,并将它们批量成单个请求发送到集群。这种批处理对于处理大量数据的应用程序的性能优化至关重要。

  • 静态使用:类似于RestHighLevelClient,当你希望有一个集中管理批量操作的组件时,使用静态的BulkProcessor是有用的。这种方法有助于调优性能并有效控制资源使用。

配置项详解

  1. BOOTSTRAP_SERVERS_CONFIG:

RETRIES_CONFIG:

  • 如果生产者发送消息失败,这定义了它可以重试发送的次数。设置为0表示不进行重试。

BATCH_SIZE_CONFIG:

  • 控制生产者批处理消息的大小,大批量可以减少请求次数,提高客户端和服务器之间的效率。

LINGER_MS_CONFIG:

  • 指定生产者在发送批次之前等待更多消息的时间,以毫秒为单位。即使批次未满,也会因为该参数设置的延迟而发送,有助于减少请求的数量但可能增加消息的延迟。

BUFFER_MEMORY_CONFIG:

  • 定义生产者可用于缓存等待发送到服务器的消息的内存总量。如果生产速度超过发送速度,将使用此缓存空间,如果缓存满了,会导致发送被阻塞。

KEY_SERIALIZER_CLASS_CONFIGVALUE_SERIALIZER_CLASS_CONFIG:

  • 分别定义了消息键和值的序列化器。这里使用的是 StringSerializer,适用于键和值都是字符串类型的场景。

MAX_BLOCK_MS_CONFIG:

  • 如果缓冲区满时,生产者的发送操作会阻塞,这个配置定义了生产者在抛出异常之前可以阻塞的最长时间,以毫秒计。

关键配置详解

  1. Consumer Factory 设置:

  • setConsumerFactory(getConsumerFactory()): 这一步设置工厂用于创建消费者的消费者工厂,此消费者工厂提供了连接Kafka所需的所有配置。

并发设置:

  • setConcurrency(concurrency): 定义了容器可以同时运行的监听器(消费者)数量。这个并发数通常和Kafka主题的分区数相匹配。

批量消费设置:

  • setBatchListener(batchListener): 决定了监听器是否应以批量模式运行。批量模式允许监听器在单次poll调用中处理多条消息,这对于提高吞吐量非常有效。

手动提交偏移量:

  • factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE): 设置手动立即提交模式,这意味着每处理完一条消息或消息批次后,应用必须显式地调用偏移量提交,这提供了最大程度的控制,有助于确保消息在处理完成后才确认。

用途和优势

  • 灵活控制:此方法通过参数 batchListener 允许选择是否批量处理消息,提供灵活的消息处理策略。

  • 高效处理:批量处理消息可以减少访问Kafka的次数,从而降低延迟,提高系统的整体吞吐量。

  • 确保数据完整性:通过手动提交偏移量,可以确保只有在消息被正确处理之后才提交偏移量,从而防止消息丢失或重复处理。

配置项详解

  1. BOOTSTRAP_SERVERS_CONFIG: 指定Kafka集群中的服务器列表,用于初始化连接到集群。

  2. ENABLE_AUTO_COMMIT_CONFIG: 设置为false表示不启用自动提交offset,这允许更精确地控制何时确认消息已被消费,常用于需要确保消息处理完成后再提交offset的场景。

  3. AUTO_COMMIT_INTERVAL_MS_CONFIG: 设置自动提交的时间间隔,以毫秒为单位。仅在启用自动提交时有效。

  4. SESSION_TIMEOUT_MS_CONFIG: 设置会话超时时间,如果在此时间内消费者未能发送心跳到broker,它会被认为已经死亡,群组将进行重新平衡。

  5. KEY_DESERIALIZER_CLASS_CONFIGVALUE_DESERIALIZER_CLASS_CONFIG: 分别设置key和value的反序列化类,这里使用的是StringDeserializer,适用于字符串格式的消息。

  6. GROUP_ID_CONFIG: 设置消费者群组ID,用于在同一个群组内的消费者之间进行负载均衡。

  7. AUTO_OFFSET_RESET_CONFIG: 设置当没有有效的offset时的重置策略。"latest"表示从最新的记录开始消费,"earliest"则表示从头开始消费。

  8. MAX_POLL_RECORDS_CONFIG: 控制单次调用poll方法返回的最大记录数,可以根据消费者的处理能力调整此值以优化性能。

功能和用途

  • 自动提交开关 (enableAutoCommit): 决定了是否自动提交消费的offset。如果为true,消费者会在指定的时间间隔后自动提交它所消费的最后一个offset。

  • 会话超时 (sessionTimeout): 如果消费者在此期间内未向服务器发送心跳,则认为其已经故障,Kafka会触发再均衡(rebalance)。

  • 自动提交间隔 (autoCommitInterval): 设置自动提交offset的频率,影响数据的重复消费和消息丢失的可能性。

  • 群组ID (groupId): 消费者所属的群组ID,用于在群组内部进行消息的负载均衡。

  • 偏移量重置 (autoOffsetReset): 设置当没有有效的初始偏移量或偏移量超出范围时,消费者应从哪里开始消费(如earliestlatest)。

  • 并发数 (concurrency): 控制消费者实例的并发线程数,可以根据主题的分区数和消费者的处理能力来配置。

cassandraClustercassandraSession 在使用 Apache Cassandra 数据库时,是两个常用的概念,它们在 Java 的 Cassandra 驱动(如 Datastax 的驱动)中扮演重要的角色。下面是它们的具体作用和用途:

1. cassandraCluster

  • 定义和作用

    • cassandraCluster 通常是指一个 Cluster 对象,它代表了客户端与 Cassandra 数据库集群的一种连接。这个对象是所有与数据库交互的起点,包括连接管理和配置。

  • 功能

    • 管理与 Cassandra 集群的连接。

    • 配置连接参数,如连接池、认证信息(用户名和密码)、SSL设置、超时时间等。

    • 允许客户端从集群中获取元数据,如节点、键空间和表信息。

2. cassandraSession

  • 定义和作用

    • cassandraSession 是基于已经建立的 Cluster 对象创建的 Session 对象。这个 Session 是执行实际的 CRUD(创建、读取、更新、删除)操作和其他数据库查询的接口。

  • 功能

    • 执行 CQL(Cassandra Query Language)语句来查询或更新数据。

    • 管理与具体键空间的连接,通常在创建 Session 时可以指定连接到特定的键空间。

    • 处理请求和获取结果,例如执行查询和接收结果集。

使用场景

在开发涉及 Cassandra 数据库的应用程序时,通常会在配置类中定义 cassandraClustercassandraSession 的 Bean。这样做的好处是:

  • 集中管理:通过 Spring 的依赖注入管理 Cassandra 的连接和会话,使得代码更加模块化和易于管理。

  • 配置分离:将数据库连接的配置与业务逻辑代码分离,便于维护和变更。

  • 资源优化:通过合理的配置连接池和会话参数,优化资源使用和提高应用性能。

简而言之,cassandraCluster 主要用于配置和维护数据库连接,而 cassandraSession 用于执行数据库操作。通过这两者的合理配置和使用,可以有效地管理 Cassandra 数据库操作,确保应用程序的性能和稳定性。

@Primary // 标记此Bean为当存在多个同类型Bean时的首选注入对象

代码段利用了Spring框架,并且通过注解来注入与Cassandra相关的特定Session bean。在Spring中,@Autowired注解用于自动依赖注入。当有多个相同类型的bean时,可以结合使用@Autowired@Qualifier注解来指定要注入的具体bean。

@ApiImplicitParams({
        @ApiImplicitParam(name = "reqJson",
                value = "",
                paramType = "body", dataType = "string"),
})
  1. @Data: 使用 Lombok 库的 @Data 注解自动生成getter和setter方法,减少样板代码。

  2. 中文注释: 所有字段添加中文注释以增强代码的可读性和易理解性。

  3. JPA和FetchType: 采用 JPA 标准对实体类进行注解,并为集合类型的字段指定懒加载策略(FetchType.LAZY),以优化性能。

  4. API文档: 使用Swagger注解详细描述每个字段的业务意义,便于API的使用和理解。

String orderId = "123456";  // 这是订单的唯一标识符
String lockKey = String.format("locked:exch:svc:%s", orderId);  // 使用格式字符串生成锁的键

RLock lock = redissonClient.getLock(lockKey);  // 获取Redisson锁实例
try {
    // 尝试获取锁,这里设置尝试锁定的最长时间为1分钟,锁自动释放的时间也为1分钟
    if (lock.tryLock(1, 1, TimeUnit.MINUTES)) {
        try {
            // 执行需要同步的操作
            // 比如更新数据库中的订单状态,处理业务逻辑等
        } finally {
            lock.unlock();  // 确保在操作完成后释放锁
        }
    } else {
        // 获取锁失败的处理逻辑
        System.out.println("Failed to acquire the lock for order: " + orderId);
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 处理中断异常
}

加群联系作者vx:xiaoda0423

显示全文