SpringBoot2+Netty打造通俗简版RPC通信框架

Springboot整合Netty实现RPC服务器 还有Java核心知识点+全套架构师学习资料和视频+一线大厂面试宝典+面试简历模板可以领取+阿里美团网易腾讯小米爱奇艺快手哔哩哔哩面试题+Spring源码合集+Java架构实战电子书+2021年最新大厂面试题。《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!ava开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**[外链图片转存中…(img-Av8lOV55-1713320457290)] 阅读详情

2019-07-19:完成基本RPC通信!

2019-07-22:优化此框架,实现单一长连接!

2019-07-24:继续优化此框架:1、增加服务提供注解(带版本号),然后利用Spring框架的在启动时立刻保存提供服务的实现类。2、优化NettyConfig(区分消费者和提供者配置),因为一个项目可同时作为服务提供者和服务消费者,所以增加两个配置来区分是提供服务还是消费服务,而且,因为如果都是本地启动着两个项目,那么IP必定是一样的,所以需要区分服务端口和消费端口。不然会有下面事故:先启动client,再启动server,但是他们同样依赖于netty包,所以client也启动了netty服务,只配置一个相同的端口会导致client的RPC通信也是通道自己启动的Netty服务。。。

2019-07-27:优化此框架:增加注册中心,使用Zookeeper作为注册中心。

接下来:我会优化Netty方面的,例如增加心跳检测、业务处理统一使用自定义业务线程池、客户端或服务端异常断开处理等,然后会优化一下项目的结构和rpc通信返回结果等,最后可能会考虑增加Redis作为注册中心。等完成所有的这些,就会对整个项目重新写一篇文章来介绍一下自己的整体思路,当然了,如果有同学需要的,可以在下方留言,我可以提前写文章,对完成注册中心及之前的代码进行详细介绍,之后再补充其他新增的功能实现过程!~

2019-07-30:已完成全部功能。放上连接:完整版RPC通信框架

下面的是2019-07-19写的文章,所以代码是没经过优化的,不过是核心代码,还是需要阅读一下的,需要看完整代码的请到最下面的github地址,大家可根据标签拉到对应的代码,麻烦啦~然后还有,测试方法是HelloController的sayHello方法呢,也可以自己再捣鼓一些测试一下~

前段时间,我花了两个星期的时间去重新学习Netty,因为之前总是看过一会就没看了,所以今次下定决心一定要全部看完,然后也思考做了一些的思考题,并且将简单的控制台版IM系统做出来了。虽然叫IM系统,但是是很简陋的,哈哈,只有登录、单聊、建群、加群、退群、群聊等简单的功能。大家可以到我github上看看:Netty-IM

写完这个IM系统后,我是打算自己写一个网页版的,可是考虑到自己前端的技能好像都退化得差不多了,而且时间上可能没那么充裕,就不了了之了。然后有一天,突然想起来之前使用的RPC框架->Dubbo,他的通信底层就是使用Netty,那么我就想着要不自己先搞个简单版试试呗,因为最主要的是学习技能得实践一番,不然学了好像没学一样。。。

在开始动手前,自己屡了一下思路,也参考了两篇文章,决定先做一个简版的RPC框架,不带注册中心的那种。那么来了老弟,首先我们看一下整个流程图是咋样的:

接下来重头戏来了,下面将会较详细得说一下流程:

先简单介绍一下项目结构:

simple-rpc-client:服务消费

simple-rpc-server:服务提供

simple-rpc-encapsulation:消费者和提供者公共接口

simple-rpc-netty:是关于Netty的东西,包括:自定义协议,序列化,通信实体Packet,各种Handler等等。

客户端:
        1、首先是两个注解,一个注解是:标识那些接口的调用会进行RPC通信,即@NettyRPC注解。
        另外一个注解是:告诉程序哪些包下的类会使用RPC通信,像@ComponentScan一样,即@EnableNettyRPC注解。

/**
 * @author Howinfun
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface NettyRPC {
}
/**
 * @author Howinfun
 * @desc
 * @date 2019/7/15
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableNettyRPC {
    //扫描的包名,如果为空,则根据启动类所在的包名扫描
    String[] basePackages() default {};
}


        2、因为我们使用@NettyRPC的将是一些接口,如果项目里头没有实现类,那是调用失败的,那么我们可以通过实现ImportBeanDefinitionRegistrar和自定义FactoryBean和InvocationHandler,利用动态代理使接口有实现,并且能动态注入Bean。ImportBeanDefinitionRegistrar接口可以详细说一下,因为这里是动态注入Bean,怎么注入规则是可以自定的,主要是靠ClassPathScanningCandidateComponentProvider这个类,它主要功能是扫描ClassPath下的所有类,并且根据isCandidateComponent方法来判断哪些类可以作为候选人,当然了,isCandidateComponent方法你可以重写,然后加上你自己的规则,我这里是必须是独立的并且是接口,才能成为候选人。然后ClassPathScanningCandidateComponentProvider还能添加过滤器,我这里主要添加的过滤器是注解过滤器,只要带有@NettyRPC注解的,其他的都不要。
    不过需要注意一点的是:记得在有@Configuration注解的配置类上使用@Import导入实现ImportBeanDefinitionRegistrar的类,不然实现动态注入Bean的作用,这里我们在客户端的启动类Import即可。

package com.hyf.rpc.netty.client.config;

import com.hyf.rpc.netty.anno.EnableNettyRPC;
import com.hyf.rpc.netty.anno.NettyRPC;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;


/**
 * 自定义注册带@NettyRPC注解的接口,利用动态代理使接口有实现
 * 然后在有@Configuration注解的配置类上使用@Import导入,不然不能注入这些实现@NettyRPC接口的BeanDefinition
 * @author Howinfun
 * @date 2019-07-18
 */
public class NettyRpcClientRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        ClassPathScanningCandidateComponentProvider scan = getScanner();

        //指定注解,类似于Feign注解,只扫描带@NettyRPC注解的接口
        scan.addIncludeFilter(new AnnotationTypeFilter(NettyRPC.class));

        Set<BeanDefinition> candidateComponents = new HashSet<>();
        for (String basePackage : getBasePackages(importingClassMetadata)) {
            candidateComponents.addAll(scan.findCandidateComponents(basePackage));
        }
        candidateComponents.stream().forEach(beanDefinition -> {
            if (!registry.containsBeanDefinition(beanDefinition.getBeanClassName())) {
                if (beanDefinition instanceof AnnotatedBeanDefinition) {
                    AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
                    AnnotationMetadata annotationMetadata = annotatedBeanDefinition.getMetadata();
                    Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(NettyRPC.class.getCanonicalName());

                    this.registerNettyRpcClient(registry, annotationMetadata,attributes);
                }
            }
        });
    }

    private void registerNettyRpcClient(BeanDefinitionRegistry registry,
                                        AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        // 指定工厂,使用@NettyRPC注解的接口,当代码中注入时,是从指定工厂获取,而这里的工厂返回的是代理
        BeanDefinitionBuilder definition = BeanDefinitionBuilder
                .genericBeanDefinition(NettyClientFactoryBean.class);
        // @Autowrie:根据类型注入
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        // 注定type属性
        definition.addPropertyValue("type", className);
        String name = attributes.get("name") == null ? "" :(String)(attributes.get("name"));
        // 别名
        String alias = name + "NettyRpcClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
        beanDefinition.setPrimary(true);
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        // 注册BeanDefinition
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }



    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                // 判断候选人的条件:必须是独立的,然后是接口
                if (beanDefinition.getMetadata().isIndependent() && beanDefinition.getMetadata().isInterface()){
                    return true;
                }
                return false;
            }
        };
    }

    /**
     * 获取指定扫描@NettyRPC注解的包路径
     * @param importingClassMetadata
     * @return
     */
    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata
                .getAnnotationAttributes(EnableNettyRPC.class.getCanonicalName());

        Set<String> basePackages = new HashSet<>();
        // 如果指定的包路径为空,则获取启动类当前路径
        if (basePackages.isEmpty()) {
            basePackages.add(
                    ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }else{
            for (String pkg : (String[]) attributes.get("basePackages")) {
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
        }
        return basePackages;
    }
}

package com.hyf.rpc.netty.client.config;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;

import java.lang.reflect.Proxy;

/**
 * @author Howinfun
 * @desc
 * @date 2019/7/15
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Component
public class NettyClientFactoryBean implements FactoryBean<Object> {

    private Class<?> type;

    @Override
    public Object getObject() throws Exception {
        // 这里的interfaces注意是就是type,因为我们现在是给接口做代理,千万别写type.getInterfaces(),不然启动会报错
        return Proxy.newProxyInstance(type.getClassLoader(),new Class[]{type},new NettyRPCInvocationHandler(this.type));
    }

    @Override
    public Class<?> getObjectType() {
        return this.type;
    }
}


        3、在动态代理的invoke方法里头,我们将启动Netty的一个客户端,带上接口调用的信息,然后等待Netty服务端返回结果结果再返回到前端即可。

package com.hyf.rpc.netty.client.config;

import com.hyf.rpc.netty.client.NettyClient;
import com.hyf.rpc.netty.packet.RPCRequestPacket;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @author Howinfun
 * @desc
 * @date 2019/7/15
 */
@NoArgsConstructor
@Component
public class NettyRPCInvocationHandler implements InvocationHandler {

    private Class<?> type;

    public NettyRPCInvocationHandler(Class<?> type){
        this.type = type;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RPCRequestPacket requestPacket = new RPCRequestPacket();
        requestPacket.setClazz(type);
        requestPacket.setMethodName(method.getName());
        requestPacket.setParamTypes(method.getParameterTypes());
        requestPacket.setParams(args);
        Object result = NettyClient.callRPC(requestPacket);
        return result;
    }
}

    有一个坑是:当客户端接收到服务端的返回结果后,记得关闭通道[ctx.channel().close()],因为在客户端中RPC调用后是同步等待Channel关闭的,不然不能响应给前端。

服务端:服务端的流程稍微会简单很多
        1、启动Netty服务端服务,然后接收客户端的链接请求,解析请求
        2、然后根据接口调用信息,利用反射获取到实现类和对应的方法,最后调用方法得到结果,然后封装一下结果就可以相应给客户端了。

package com.hyf.rpc.netty.server.handler;

import com.hyf.rpc.netty.packet.RPCRequestPacket;
import com.hyf.rpc.netty.packet.RPCResponsePacket;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.reflections.Reflections;

import java.lang.reflect.Method;
import java.util.Set;

/**
 * @author Howinfun
 * @desc
 * @date 2019/7/16
 */
@ChannelHandler.Sharable
public class RPCRequestPacketHandler extends SimpleChannelInboundHandler<RPCRequestPacket> {

    public static final RPCRequestPacketHandler INSTANCE = new RPCRequestPacketHandler();
    private RPCRequestPacketHandler(){}

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RPCRequestPacket msg) throws Exception {
        RPCResponsePacket responsePacket = new RPCResponsePacket();
        // 获取rpc调用信息,利用反射执行方法,返回结果
        Class clazz = msg.getClazz();
        String methodName = msg.getMethodName();
        Object[] params = msg.getParams();
        Class[] paramTypes = msg.getParamTypes();
        // 扫面路径下所有元数据
        Reflections reflections = new Reflections("com.hyf.rpc.serviceImpl");
        Set<Class> subTypes = reflections.getSubTypesOf(clazz);
        if (subTypes.isEmpty()){
            responsePacket.setSuccess(false);
            responsePacket.setMsg("没有实现类");
        }else if (subTypes.size() > 1){
            responsePacket.setSuccess(false);
            responsePacket.setMsg("多个实现类,无法判断执行哪一个");
        }else{
            Class subClass = subTypes.toArray(new Class[1])[0];
            Method method = subClass.getMethod(methodName,paramTypes);
            Object result = method.invoke(subClass.newInstance(),params);
            responsePacket.setSuccess(true);
            responsePacket.setResult(result);
        }
        ctx.channel().writeAndFlush(responsePacket);
    }
}


        3、这里的反射我推荐一个很好用的框架->Reflections。简单介绍一下我使用了哪些API,首先是根据路径扫描反射元数据,
    然后根据接口获取它的所有实现类,然后就可以获取实现类的反射信息,得到方法执行结果了。

如果同学们对此比较简陋的代码还略感兴趣,可以到我github上看看:Netty-RPC

Spring Boot整合Dubbo与Netty的后端架构模板设计 微服务架构作为现代分布式系统设计的核心范式,强调将单一应用程序拆分为多个独立、松耦合的服务模块,从而提升系统的可维护性、可扩展性与部署灵活性。在Java生态中,凭借其“约定优于配置”的理念、快速启动能力和丰富的Starter组件,成为构建微服务的首选框架。Spring Boot 的核心设计理念在于简化Spring应用的初始搭建与开发。其自动配置机制(Auto-Configuration)通过条件注解(如。 阅读详情

相关推荐

spring boot +maven+ netty4+protostuff+zookeeper实现一个轻量级RPC框架

Spring:它是最强大的依赖注入框架,也是业界的权威标准。 Netty:它使 NIO 编程更加容易,屏蔽了 Java 底层的 NIO 细节。 Protostuff:它基于 Protobuf 序列化框架,面向 POJO,无需编写 .proto 文件。 ZooKeeper:提供服务注册与发现功能,开发分布式系统的必备选择,同时它也具备天生的集群能力。

SpringBoot整合Netty(服务端)

Netty是由JBOSS提供的一个Java开源框架,现为Github上的独立项目。它是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络IO程序。Netty主要针对在TCP协议下,面向Clients端的高并发应用,或者Peer-to-Peer场景下的大量数据持续传输的应用。Netty提供了一套完整的API,用于处理网络IO操作,如TCP和UDP套接字。它封装了底层的网络编程细节,使得开发者可以更加专注于业务逻辑的实现。

m0_70554089的博客 9167

springboot整合netty,分客户端和服务端两个项目

springboot整合netty,分客户端和服务端两个项目,springboot整合netty,分客户端和服务端两个项目,springboot整合netty,分客户端和服务端两个项目,springboot整合netty,分客户端和服务端两个项目

微服务高效通讯方案之SpringBoot + gRpc + Protobuf + Netty

微服务高效通讯方案之gRpc初识什么是gRpc功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 初识 什么是gRpc g...

Mirson - 多一些分享, 多一些进步 3025

grpc-spring-boot-starter:用于gRPC框架的Spring Boot启动器模块

gRPC Spring Boot Starter 自述:| 文档:| 产品特点 使用@GrpcService实现自动配置并运行gRPC服务器 使用@GrpcClient自动创建和管理您的grpc频道和存根 支持其他grpc-java风格(例如 , ,...) 服务器端:应该适用于所有grpc-java版本(基于io.grpc.BindableService ) 客户端:需要自定义StubFactory 当前内置支持: grpc-java (请报告丢失的内容,以便我们为它们添加支持) 支持 支持 服务器端:将grpc-port信息添加到服务注册详细信息当前本地支持: (请报告丢失的内容,以便我们为它们添加支持) 客户端:从spring的DiscoveryClient读取服务的目标地址(所有类型) 支持作为分布式跟踪解决方案(如果存在 ) 支持全局和自定义gRPC服务器/客户端拦截器 自动公制支持(基于/ ) 也适用于(非阴影)grpc-netty 版本号 2.xxRELEASE支持Spring Boot 2.1.x / 2.2.x和Spring Cloud Gr

一款基于Netty+Zookeeper+Spring实现的轻量级Java RPC框架。提供服务注册,发现,负载均衡,.zip

一款基于Netty+Zookeeper+Spring实现的轻量级Java RPC框架。提供服务注册,发现,负载均衡,

基于Netty和SpringBoot实现一个轻量级RPC框架-协议篇

最近对网络编程方面比较有兴趣,在微服务实践上也用到了相对主流的框架如底层也切换为,像底层也是使用封装通讯协议,最近调研和准备使用的也是基于封装实现了多种协议的兼容。因此,基于造一个轮子,在的加持下,实现一个轻量级的框架。这篇博文介绍的是框架协议的定义以及对应的编码解码处理的实现。截止本文()编写完成之时,的最新版本为,而的最新版本为,因此引入这两个版本的依赖,加上其他工具包和序列化等等的支持,文件的核心内容如下: 部分参数的序列化会依赖到或者,具体看偏好而定。为了提高协议传输的效率,需要定制一套高效的协议,

Throwable 608

在SpringBoot中整合使用Netty框架的详细教程

主要介绍了在SpringBoot中整合使用Netty框架的教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

手写通俗简版RPC通信框架,你上你也行!

使用SpringBoot2+Netty+Zookeeper,从0到1手写RPC通信框架,所谓手写框架,你上你也行!

Howinfun的博客 951

C# WinForm和Sherlock进行对接

文章目录 前言 一、使用步骤 二、使用步骤 1.引入库 2.读入数据 总结 前言 本人公司最近在做一款瑕疵检测的视觉项目,项目中利用Sherlock从相机中取图和对图像进行视觉处理,然后将图像和数据传输到项目中。这过程中软件需要和Sherlock进行图像和数据的传递,这就需要WinForm和Sherlock进行对接,但我在网上查找了许久能用上的资料十分有限,所以我决定写下这篇文章,来做一次总结,同时也希望能帮助到同样在为此问题困扰的你。 一、使用步骤 废话不...

adminmmrt的博客 3221

内窥镜胶囊(胶囊内镜)硬件方案

内窥镜胶囊(胶囊内镜)硬件方案 前言 说明:该方案为作者2018年上半年完成的第一版,后来搁置了一段时间,才重启这个项目。目前(2020.07)第二版已经快要完成。[联系v:1#7#6#3#3#3#5#0#8#7#0]先给一下第一版和第二版的基本参数。 第一版: 续航时间:3h 帧率:0.5fps 尺寸:12mm * 25mm 角度:45° 第二版: 续航时间:12h 帧率:2-6fps 尺寸:9.8mm * 25mm 角度:130° 第一版属于尝试版,整体方案不是很成熟。第二版这个数据就非常优秀了。我拿了

CrekerLi的博客 4780

探秘Easy-RPC:轻量级Java RPC框架,让服务调用变得简单!

探秘Easy-RPC:轻量级Java RPC框架,让服务调用变得简单! 项目地址:https://gitcode.com/gh_mirrors/ea/easy-rpc 如果您正在寻找一款易于理解和使用的Java RPC框架,那么这个基于Netty、Zookeeper和Spring构建的轻量级框架——Easy-RPC,将是您的理想选择。它不仅提供了基础的RPC功能,还是学习RPC工作原理和Java编...

gitblog_00057的博客 598

本地部署静态网站托管平台 Netlify 并实现外部访问(Windows 版本)

Netlify 是一款基于云计算的网站部署和托管平台,专为开发者提供便捷的静态网站和 Jamstack 应用部署服务。它的能力在于将网站构建、部署、托管以及持续集成等流程高度集成,简化了开发和运维的复杂程度。用户无需关心服务器的配置、维护等工作。本文将详细介绍如何在本地安装 Netlify 以及结合路由侠内网穿透实现外网访问 Netlify。

lyxnwct的博客 524

RPC的通信Netty的底层是Nio,赶紧收藏!

如何使用Spring Boot构建微服务体系 通过本文内容的学习,你将循序渐进的学习到Spring Boot微框架的设计理念和原理,并对框架重点功能和模块进行逐一详解;其次,你将会学习到如何基于Spring Boot微框架构建一套完整的微服务体系;最后总结Spring Boot相关内容,以温故知新。 文档内容分为七大模块,为了方便大家阅读,小编就以截图展示部分内容 第1章:了解微服务 SpringBoot是一个可使用Java构建微服务的微框架,所以在了解SpringBoot之前,我们需要先了解什么是微服务

weixin_57992300的博客 88

EM4095低频RFID曼彻斯特解码实战:从原理图到MCU代码实现

本文详细介绍了EM4095低频RFID读卡器的设计与实现,从芯片工作原理、天线设计到曼彻斯特解码算法。重点讲解了基于STM32的MCU代码实现,包括定时器捕获、数字滤波和错误处理,为开发125kHz RFID系统提供实用指南。

oil88的博客 715

使用n8n搭建服务器监控系统:从Webhook到Telegram告警的完整实现

本文介绍基于n8n工作流平台搭建轻量级服务器监控系统的完整方案,通过Webhook接收监控数据、Token认证、阈值检查和Telegram告警推送,实现7x24小时自动化监控。整个系统由7个可视化节点组成,支持CPU、内存、磁盘、网络流量等多维度指标监控,告警阈值可灵活配置。相比传统的Prometheus或Zabbix方案,配置更简单、资源消耗更低,适合1-50台中小规模服务器的监控需求。实测表明从部署到上线仅需30分钟,告警响应延迟小于1秒,完全满足生产环境要求。

todoitbo的博客 1万+

毕业设计写这个你就赢了《Netty + JavaFx 实战:仿桌面版微信聊天》

一、前言 本项目是作者小傅哥使用JavaFx、Netty4.x、SpringBoot、Mysql等技术栈和偏向于DDD领域驱动设计方式,搭建的仿桌面版微信聊天工程实现通信核心功能。如果本项目能为您提供帮助,请给予支持(关注、点赞、分享)! 雨后天晴写下、年华, 巫山云景彩霞、如画。 心似平远走码、飞驾, 正装年少风华、正恰。 走过了一个漫长的假期,从年假的第一天开始因为不能但又不能让自己太闲...

bugstack虫洞栈 5426

保研考研机试攻略:第一章——从零开始

🧊保研考研机试攻略:第一章——从零开始

qq_63349644的博客 1321

程序员为什么热衷于造轮子,升职加薪吗?

一、前言 哪个架构师没造过轮子? 你想过这样一件事吗?是先具备能力再安排职位,还是先安排职位在学习?什么?走后门,你出去! 就像我们上学考试、跆拳道考段、晋升答辩一样,都是先具备了可胜任上一阶段的能力,才给予相应的职位。所以,架构师造轮子从做程序员时候就开始了,只不过到了架构师阶段可以造出更好的轮子。 鉴于实际业务开发的紧急程度,不会允许你造轮子。但造轮子,几乎是每个程序员突破技术瓶颈的最佳方式。千万不要因为一句,不要重复造轮子的借口,给自己一个不学习的理由! 二、我造过的轮子 我可...

马小婧QAQ 319

Python 爬虫实战:解析 JSON 数据爬取今日头条热点新闻(API 接口逆向与字段提取)

在当今信息爆炸的时代,新闻资讯的获取变得尤为重要。今日头条作为国内领先的新闻聚合平台,其热点新闻数据具有很高的价值。本文将详细介绍如何通过 Python 爬虫技术,逆向解析今日头条的 API 接口,提取并解析 JSON 数据,最终获取热点新闻信息。

yansideyucsdn的博客 3210

079、LVGL基础控件:消息框(Msgbox)

《LVGL消息框控件深度解析与避坑指南》 文章深入剖析了LVGL消息框控件的核心特性和使用技巧。消息框本质是带按钮的模态容器,而非独立窗口。作者分享了创建消息框的正确姿势、标题/内容设置方法、安全的按钮事件处理方式(避免直接调用close函数导致栈溢出),并详细介绍了模态/非模态实现、样式自定义、动态更新等实用技巧。针对常见问题如内存管理、按钮数量限制、动画效果等给出了专业建议,特别强调了消息框作为用户交互关键环节的优化重要性,建议通过异步删除、合理父对象选择等方式提升稳定性。文章包含大量实际项目经验总结,

m0_50546716的博客 153

程序员为什么热衷于造轮子,而不是硬照,升职加薪吗?

一、前言 哪个架构师没造过轮子? 你想过这样一件事吗?是先具备能力在安排职位,还是先安排职位在学习? ????什么?走后门,你出去! 就像我们上学考试、跆拳道考段、晋升答辩一样,都是先具备了可胜任上一阶段的能力,才给予相应的职位。所以,架构师造轮子从做程序员时候就开始了,只不过到了架构师阶段可以造出更好的轮子。 鉴于实际业务开发的紧急程度,不会允许你造轮子。但造轮子,几乎是每个程序员突破技术瓶颈的最佳方式。千万不要因为一句,不要重复造轮子的借口,给自己一个不学习的理由! 二、我造过的轮子 我可能

weixin_45820912的博客 374

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Dubbo基本概念与管理后台项目的运行

Appache Dubbo是一款高性能、轻量级的开源Java RPC分布式服务框架,它提供了三大核心能力: 面向接口的远程方法调用,即可以使用@Resource注解可以调用远程的Service,就像调用本地方法一样调用远程方法; 智能容错和负载均衡; 服务的自动注册与发现,dubbo注册中心调用zookeeper实现。 Dubbo底层通信通过对NIO框架Netty进行封装。 dubbo中组件角色: Provider,提供服务的服务提供方 Consumer,调用远程服务的服务消费方 Registry:服

XYZ 496

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

极客技术雷达

这篇博文汇总最前沿的极客技术栈。

极客星云-优快云博客 948

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

优秀架构师修炼之路-成长路线

第一阶段:开源框架源码剖析第二阶段:Web服务器深度解析及调优第三阶段:分布式架构设计&微服务深入剖析第四阶段:大型分布式存储系统架构进阶(一)第五阶段:大型分布式存储系统架构进阶(二)第六阶段:分布式消息中间件进阶第七阶段:分布式搜索引擎进阶第八阶段:分布式实时流计算引擎第九阶段:容器技术&CICD、DevOps第十阶段:底层调优算法深入第十一阶段:大型互联网项目实战和业务解决方案第十二阶段:进大厂必备-面试求职深主辅导

程序员-学习园地 595

一到五年Java开发经验的程序员如何达到年薪40W?

前言在这个行业参加工作4年到5年的攻城狮的朋友们,相信你在自己所钻研的领域已经有了自己一定的见解,这个时候,技术上你应该已经遇到瓶颈了。这个时候不要着急提高自己的技术,已经是时候提高你的影响力了,你可以尝试去一些知名的公司去提高你的背景,你可以发表一些文章去影响更多的人。当然,你也可以去Github创建一个属于你的开源项目,去打造自己的产品。技术学到这个阶段,很容易遇到瓶颈,而且往往达到一定程度后...

weixin_34406796的博客 130
上一篇: JAVA NIO[ServerSocketChannel与SocketChannel]实现服务端客户端通信
下一篇: 手写通俗简版RPC通信框架,你上你也行!
Howinfun
博客等级 码龄10年 165粉丝 213原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值