[译]什么是长轮询?

原文链接:https://www.pubnub.com/guides/long-polling/

什么是长轮询?

长轮询用于实时 Web 应用程序,以实现客户端和 Web 服务器之间近乎即时的通信。它在实时更新至关重要的聊天和消息应用程序中特别有用。

在传统的HTTP通信中,客户端向服务器发送新请求并等待响应。这称为短轮询。然而,在实时场景中,短轮询可能效率不高,因为它需要频繁向服务器请求,导致不必要的网络开销和增加延迟。

More »

[译]如何通过 WebSocket 将 Kafka 消息流式传输到面向互联网的客户端

原文链接:https://ably.com/topic/websockets-kafka

Apache Kafka 是目前最强大的异步消息传递技术之一。 Kafka 由 Jay Kreps、Jun Rao 和 Neha Narkhede 等团队于 2010 年在 LinkedIn 设计,并于 2011 年初开源。如今,该工具被众多公司(包括科技巨头,例如 Slack、Airbnb 或 Netflix 使用)为其实时数据流管道提供支持。

More »

ThingsBoard的领域模型

ThingsBoard是一个开源的物联网平台,用于管理和监控物联网设备和数据。它提供了一个可扩展的架构,可以连接各种设备,并实时收集、处理和分析设备生成的数据。

领域模型

在ThingsBoard中,领域模型是一个关键概念,用于描述物联网系统中的物理实体、属性和行为。以下是ThingsBoard中的主要领域模型组件:

  1. 租户(Tenant):租户是ThingsBoard中的顶级组织单位。它代表了一个独立的实体,可以是一个用户、组织或公司。租户拥有和管理自己的设备、客户、规则和仪表板等资源。租户之间的数据和配置是相互隔离的,每个租户都有自己的独立环境。租户由系统管理员创建和管理。

  2. 租户配置(TenantProfile):用于定义租户级别的配置和属性。

  3. 客户(Customer):客户是租户下的子级实体,代表了物联网系统中的用户或组织。一个租户可以包含多个客户,每个客户都有自己的访问权限和角色。客户可以访问和监控租户下的设备和数据。客户可以有自己的设备、规则和仪表板等资源,但这些资源受到租户级别的限制。客户由租户管理员创建和管理。

  4. 用户(User):保存用户的基本信息

    1. 用户的角色(Authority):SYS_ADMIN、TENANT_ADMIN、CUSTOMER_USER、REFRESH_TOKEN、PRE_VERIFICATION_TOKEN
    2. 用户凭证(UserCredentials):保存用户的密码、激活用户 Token、重置密码 Token、密码使用历史
    3. 用户认证设置(UserAuthSettings):保存用户 2FA 认证设置
    4. 用户设置(UserSettings):保存用户的设置,包括:通用设置、通知、访问过的仪表盘等等
  5. 系统设置(AdminSettings):是用于配置和管理整个系统的全局设置和参数,包括:通用设置、邮件、JWT、连接设置等等。

    More »

TBMQ测试和源码分析

  1. 启动 TBMQ 程序,浏览器访问 http://localhost:8083,创建一个 Application,Credentials Type 为 BASIC,客户端 ID、用户名和密码均设置为 tbmq_app

    More »

2024-02-20|RateLimitAspect请求限流、调整spring-cloud-examples项目结构

今天做了什么:

  1. ChatGPT 编写一个 RateLimitAspect 类,实现基于用户的 get 查询请求的限流功能

    @Aspect
    @Component
    public class RateLimitAspect {
      private final RedisTemplate<String, Object> redisTemplate;
      private final Logger logger = LoggerFactory.getLogger(RateLimitAspect.class);
    
      private final int maxRequests; // Maximum number of requests
      private final int timeWindow; // Time window in seconds
    
      @Autowired
      public RateLimitAspect(RedisTemplate<String, Object> redisTemplate) {
          this.redisTemplate = redisTemplate;
          this.maxRequests = 100; // Default maximum number of requests is 100
          this.timeWindow = 60; // Default time window is 60 seconds
      }
    
      @Before("@annotation(getMapping)")
      public void applyRateLimit(JoinPoint joinPoint, GetMapping getMapping) {
          HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
             .getRequestAttributes()).getRequest();
          if (request != null && HttpMethod.GET.matches(request.getMethod())) {
              String username = request.getUserPrincipal().getName(); // Get the username
              if (username != null && !username.isEmpty()) {
                  String rateLimitKey = "rate_limit:" + username;
                  incrementAndCheckRateLimit(rateLimitKey, request);
              }
          }
      }
    
      private void incrementAndCheckRateLimit(String rateLimitKey, HttpServletRequest request) {
          Long currentRequests = redisTemplate.execute((RedisCallback<Long>) connection -> {
              Object nativeConnection = connection.getNativeConnection();
              String script = "local current = redis.call('INCR', KEYS[1])\n"
                      + "if tonumber(current) == 1 then\n"
                      + "    redis.call('EXPIRE', KEYS[1], ARGV[1])\n"
                      + "end\n"
                      + "return current";
              return (Long) ((RedisOperations<?, ?>) nativeConnection).execute(
                      (RedisCallback<Object>) connection1 -> connection1.eval(
                              script.getBytes(),
                              redisTemplate.getKeySerializer(), // Use custom Key serializer
                              redisTemplate.getValueSerializer(), // Use custom Value serializer
                              Collections.singletonList(rateLimitKey.getBytes()),
                              Collections.singletonList(String.valueOf(timeWindow).getBytes())
                      )
              );
          });
    
          if (currentRequests > maxRequests) {
              String urlWithParams = getRequestUrlWithParams(request);
              logger.warn("Rate limit exceeded for key: {}. Request URL with Params: {}", 
                 rateLimitKey, urlWithParams);
              throw new RateLimitException();
          }
      }
    
      private String getRequestUrlWithParams(HttpServletRequest request) {
          String url = request.getRequestURL().toString();
          String queryString = request.getQueryString();
          if (queryString != null && !queryString.isEmpty()) {
              url += "?" + queryString;
          }
          return url;
      }
    }
    
  2. 调整 spring-cloud-examples 目录,通过源码分析 SpringBoot 2.7.18 启动过程

    More »