2024-09-03

Nginx、Apache和Tomcat都是web服务器,但它们各有优势,适用于不同的场景。

Nginx:

优点:

  1. 高性能,处理静态内容快,能够承载高流量。
  2. 负载均衡能力强,可以有效分配请求到不同的服务器。
  3. 配置简单,易于维护。
  4. 0-7680端口,专门用于监听HTTP请求。
  5. 可作为反向代理服务器,负责分发请求到后端服务器。

缺点:

  1. 不适合执行大量CPU密集型任务。
  2. 不支持Java等服务器端脚本语言。

Apache:

优点:

  1. 成熟稳定,广泛应用于各种web项目。
  2. 模块丰富,可以通过各种模块扩展功能。
  3. 支持各种脚本语言,如PHP、Python等。
  4. 可以直接处理动态内容。

缺点:

  1. 处理静态内容时不如Nginx快。
  2. 在高并发下性能不如Nginx。
  3. 占用更多系统资源。

Tomcat:

优点:

  1. 支持Servlet和JSP,Java程序员的首选。
  2. 动态内容处理能力强,适合运行Java应用。
  3. 可以直接运行WAR包,部署方便。

缺点:

  1. 性能不如Nginx,不适合高并发请求。
  2. 占用系统资源较多。
  3. 需要JVM环境,占用更多资源处理Java程序。
2024-09-03

在这个实战中,我们将完成Nacos配置中心和服务发现的整合,并通过一个简单的示例来演示如何使用。

  1. nacos-config-client模块的bootstrap.properties文件中配置Nacos服务器地址和应用名:



spring.application.name=nacos-config-client
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=8e0adefb-0153-4723-9768-993ba762166a
spring.cloud.nacos.config.group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].data-id=nacos-config-client.properties
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true
  1. nacos-config-client模块中添加一个Controller来获取配置信息:



@RestController
public class ConfigController {
 
    @Value("${useLocalCache:false}")
    private boolean useLocalCache;
 
    @GetMapping("/useLocalCache")
    public boolean getUseLocalCache() {
        return useLocalCache;
    }
}
  1. 启动nacos-config-client模块,并访问http://localhost:8080/useLocalCache,你将看到输出与你在Nacos中设置的配置相匹配。
  2. nacos-discovery-client模块中添加对服务提供者的调用:



@RestController
public class TestController {
 
    @Autowired
    private RestTemplate restTemplate;
 
    @GetMapping("/test")
    public String test() {
        return restTemplate.getForObject("http://nacos-provider/test", String.class);
    }
}
  1. 启动nacos-discovery-client模块,并访问http://localhost:8080/test,你将看到来自服务提供者的响应。

这样,你就完成了Nacos配置中心和服务发现的整合,并通过RestTemplate展示了服务间的调用。在实际应用中,你可以根据需要进一步扩展和定制。

2024-09-03



import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
import javax.sql.DataSource;
 
@Configuration
public class DruidConfig {
 
    @Value("${spring.datasource.url}")
    private String dbUrl;
 
    @Value("${spring.datasource.username}")
    private String username;
 
    @Value("${spring.datasource.password}")
    private String password;
 
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
 
    @Value("${spring.datasource.initial-size}")
    private int initialSize;
 
    @Value("${spring.datasource.min-idle}")
    private int minIdle;
 
    @Value("${spring.datasource.max-active}")
    private int maxActive;
 
    @Value("${spring.datasource.max-wait}")
    private int maxWait;
 
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(dbUrl);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driverClassName);
 
        //配置初始化大小、最小、最大
        dataSource.setInitialSize(initialSize);
        dataSource.setMinIdle(minIdle);
        dataSource.setMaxActive(maxActive);
 
        //配置获取连接等待超时的时间
        dataSource.setMaxWait(maxWait);
 
        return dataSource;
    }
}

这段代码展示了如何在Spring Boot 3项目中配置Druid数据源。通过@Value注解,我们可以将配置文件中的数据库连接信息注入到对应的字段中。然后,我们创建了一个名为dataSource的Bean,该Bean会被Spring框架用来创建数据库连接。这个数据源被配置了初始化连接数、最小空闲连接和最大活跃连接数,以及获取连接时的等待超时时间。这样,我们就可以通过Druid提供的功能来监控数据库性能和管理数据库连接。

2024-09-03



import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
 
@Aspect
@Component
public class LoggingAspect {
    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAspect.class);
 
    @Pointcut("execution(* com.example.service.YourService.*(..))") // 根据实际情况修改包名和类名
    public void serviceLayerMethods() {
    }
 
    @Before("serviceLayerMethods()")
    public void logBefore(JoinPoint joinPoint) {
        LOGGER.info("Method: {}", joinPoint.getSignature().getName());
        // 打印参数等其他信息
    }
 
    @AfterReturning(pointcut = "serviceLayerMethods()", returning = "result")
    public void logAfterReturning(Object result) {
        LOGGER.info("Result: {}", result);
    }
 
    @AfterThrowing(pointcut = "serviceLayerMethods()", throwing = "ex")
    public void logAfterThrowing(Exception ex) {
        LOGGER.error("Exception: {}", ex.getMessage());
    }
}

这个示例代码展示了如何在Spring Boot应用中使用AOP来记录服务层(YourService所在包)的方法调用。这里使用了@Pointcut注解来定义一个切入点,@Before注解来在方法执行前记录日志,@AfterReturning注解来在方法成功返回后记录结果,以及@AfterThrowing注解来在方法抛出异常后记录异常信息。这个例子提供了一个简单的日志记录框架,可以根据实际需求进行扩展和定制。

2024-09-03



import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
 
@Repository
public interface CountryRepository extends JpaRepository<Country, Long> {
 
    @Query("SELECT c FROM Country c WHERE c.name = :name")
    Country findByName(@Param("name") String name);
 
    // 其他查询方法
}

这个代码实例展示了如何在SpringBoot应用中定义一个简单的Country仓库接口,用于查询特定的Country实体。这里使用了Spring Data JPA的注解,它简化了数据库访问层的代码编写。@Repository注解标注该接口,使得Spring能够自动扫描并注册该仓库。findByName方法通过方法命名约定自动生成查询,使得开发者能够以更声明式的方式编写数据访问逻辑。

2024-09-03

报错问题解释:

Kubernetes (k8s) 部署 Spring Boot 项目时,遇到内存持续增长的问题,可能是因为内存泄漏、无限循环或者不当的资源配置。

解决方法:

  1. 检查代码:确认是否有可能导致内存泄漏的代码,比如没有正确关闭的资源、无限循环或者缓存未妥善处理。
  2. 监控日志:查看容器的日志,观察是否有异常或错误信息输出。
  3. 分析堆内存:使用诸如JVisualVM, JProfiler, 或者MAT (Memory Analyzer Tool) 等工具分析堆内存的使用情况,查找内存泄漏的源头。
  4. 调整资源配额:检查Kubernetes部署的配置文件,确保为Spring Boot项目设置了合理的内存请求(requests)和限制(limits)。
  5. 优化代码:优化代码逻辑,减少不必要的对象创建,使用缓存等策略提高性能。
  6. 更新依赖:检查并更新项目中的依赖库,确保使用的是最新的稳定版本。

在调整配置或代码后,重新部署应用并监控内存使用情况,确保问题得到解决。

2024-09-03

Spring Boot 使用 WebSocket 的几种方式:

  1. 使用 Spring 原生的 WebSocketMessageBrokerConfigurer 接口。
  2. 使用 STOMP 子协议。
  3. 使用 SockJS 库提供跨浏览器支持。

高并发问题及解决方案:

  1. 连接数过多:WebSocket 连接本质上是长连接,会占用服务器资源。高并发场景下,服务器可能无法处理所有的连接。

    • 解决方案:使用服务器集群或者负载均衡,分散连接压力。
  2. 消息广播性能瓶颈:如果需要广播消息到所有客户端,性能可能会下降。

    • 解决方案:使用分布式或者消息队列来缓解消息压力,客户端订阅相关的 Topic。
  3. 内存泄漏:长时间运行的 WebSocket 会话可能导致内存泄漏。

    • 解决方案:定期清理无效的会话,或者使用有状态的服务来管理会话状态。
  4. 网络问题:网络不稳定可能导致 WebSocket 连接断开。

    • 解决方案:实现重连机制,网络恢复时自动重新连接。
  5. 安全问题:WebSocket 可能被用于 DDoS 攻击。

    • 解决方案:实现合理的连接限流,例如同一个 IP 的连接数限制。

具体代码示例:




@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }
 
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").withSockJS();
    }
}

在这个配置中,我们定义了一个 /topic 的消息代理,所有以 /app 开头的消息都会通过这个代理进行广播。同时,我们定义了一个 WebSocket 的端点 /ws,客户端可以通过这个端点与服务器建立 SockJS 通信。

2024-09-03

以下是一个基于Nginx实现的Tomcat动静分离和负载均衡的配置示例:

  1. Nginx配置文件(nginx.conf):



user  nginx;
worker_processes  1;
 
events {
    worker_connections  1024;
}
 
http {
    include       mime.types;
    default_type  application/octet-stream;
 
    # 日志格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    # 访问日志
    access_log  logs/access.log  main;
 
    # 静态文件缓存设置
    sendfile        on;
    keepalive_timeout  65;
 
    # 用于动静分离
    server {
        listen       80;
        server_name  localhost;
 
        # 静态内容
        location ~* \.(jpg|jpeg|gif|png|css|js|ico|html)$ {
            root   /path/to/static/files;
            expires 30d;
        }
 
        # 代理动态内容到Tomcat服务器
        location / {
            proxy_pass http://tomcat_server;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
 
    # 负载均衡配置
    upstream tomcat_server {
        server tomcat1.example.com:8080;
        server tomcat2.example.com:8080;
    }
}

在这个配置中,Nginx作为反向代理服务器接收客户端的HTTP请求,并根据location指定的规则来处理静态文件请求或将动态内容代理到Tomcat服务器。upstream模块定义了一个名为tomcat_server的服务器组,该组包含两个Tomcat实例,Nginx将动态内容负载均衡地分发到这两个实例上。

  1. 确保Nginx配置没有错误:



nginx -t
  1. 如果没有错误,重新加载Nginx配置:



nginx -s reload

这样,Nginx就会按照新的配置来接收和处理请求,实现动静分离和负载均衡。

2024-09-03

MyBatis 动态代理是一种技术,它允许你在不编写实现类的情况下,通过接口调用数据库操作。这是通过使用 Java 的动态代理功能实现的。

MyBatis 动态代理的基本原理是:

  1. 使用 JDK 动态代理,为 Mapper 接口创建一个代理对象。
  2. 当通过代理对象调用方法时,MyBatis 会生成对应的 SQL 语句并执行。

这里是一个简单的例子,演示如何使用 MyBatis 动态代理:




// Mapper 接口
public interface UserMapper {
    User selectUserById(int id);
}
 
// 测试代码
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);

在这个例子中,我们没有为 UserMapper 接口提供任何实现类,但是我们仍然能够通过 sqlSession.getMapper(UserMapper.class) 获取到 UserMapper 接口的代理对象,并调用 selectUserById 方法。

MyBatis 会在内部处理这个调用,生成相应的 SQL 语句,并执行。这就是 MyBatis 动态代理的工作方式。

2024-09-03

在Spring Boot中,你可以通过以下方法指定外部配置文件:

  1. 命令行参数: 启动Spring Boot应用时,可以通过--spring.config.location参数指定外部配置文件的路径。



java -jar yourapp.jar --spring.config.location=file:/path/to/config/
  1. 环境变量: 可以设置环境变量SPRING_CONFIG_LOCATION来指定配置文件路径。



export SPRING_CONFIG_LOCATION=file:/path/to/config/
java -jar yourapp.jar
  1. 在application.properties或application.yml中指定: 可以在Spring Boot的默认配置文件中指定外部配置文件的路径。



spring.config.import=file:/path/to/config/application.properties

或者使用YAML格式:




spring:
  config:
    import: file:/path/to/config/application.yml
  1. 在程序中指定: 在程序启动的时候,可以通过Java系统属性来指定配置文件。



System.setProperty("spring.config.location", "file:/path/to/config/");

确保路径指向正确的配置文件,并且文件必须是一个有效的Spring Boot配置文件,可以是application.propertiesapplication.yml