Easy-RPC开源项目学习-服务端

项目地址:https://gitcode.com/shaogezhu/easy-rpc


一、自定义注解

使用注解@EasyRpcService用于声明服务提供者。

二、服务端自动配置类

代码如下,主要属性是一个日志记录工具类Logger 和一个上下文管理类ApplicationContext

public class RpcServerAutoConfiguration implements InitializingBean, ApplicationContextAware {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcServerAutoConfiguration.class);

    private ApplicationContext applicationContext;

    @Override
    public void afterPropertiesSet() throws Exception {...}

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {...}

    private void printBanner() {...}
}

首先RpcServerAutoConfiguration自动配置类实现了InitializingBeanApplicationContextAware 接口。第一个接口的作用是,当Spring装载完Bean后自动调用该类中的afterPropertiesSet()方法。第二个接口作用为获取容器上下文。

1.afterPropertiesSet()方法

在该方法内进行服务的注册和设置。

    @Override
    public void afterPropertiesSet() throws Exception {
        Server server = null;
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(EasyRpcService.class);
        if (beanMap.size() == 0) {
            //说明当前应用内部不需要对外暴露服务
            return;
        }
        printBanner();
        long begin = System.currentTimeMillis();
        server = new Server();
        // 加载配置
        server.initServerConfig();
        for (String beanName : beanMap.keySet()) {
            Object bean = beanMap.get(beanName);
            EasyRpcService easyRpcService = bean.getClass().getAnnotation(EasyRpcService.class);
            ServiceWrapper dataServiceServiceWrapper = new ServiceWrapper(bean, easyRpcService.group());
            dataServiceServiceWrapper.setServiceToken(easyRpcService.serviceToken());
            dataServiceServiceWrapper.setLimit(easyRpcService.limit());
            dataServiceServiceWrapper.setWeight(easyRpcService.weight());
            // 服务注册
            server.registyService(dataServiceServiceWrapper);
            LOGGER.info(">>>>>>>>>>>>>>> [easy-rpc] {} export success! >>>>>>>>>>>>>>> ", beanName);
        }
        ServerShutdownHook.registryShutdownHook();
        // 启动服务器
        server.startServerApplication();
        long end = System.currentTimeMillis();
        LOGGER.info(" ================== [{}] started success in {}s ================== ",
                SERVER_CONFIG.getApplicationName(), ((double) end - (double) begin) / 1000);
    }

首先第一步是通过applicationContext上下文管理工具拿到使用自定义服务端注解修饰的类,如果获取的类集合为0,则代表当前没有服务需要对外暴露。

	Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(EasyRpcService.class);
		if (beanMap.size() == 0) {
		    //说明当前应用内部不需要对外暴露服务
		    return;
	 }

第二步打印控制台信息,并实例化一个Netty服务端对象,该对象中主要有四个方法,startServerApplication()initServerConfig()batchExportUrl()registyService(ServiceWrapper serviceWrapper)

	 printBanner();
	 long begin = System.currentTimeMillis();
	 server = new Server();

在这里插入图片描述
第三步根据本地配置文件加载服务器端的配置。

	public void initServerConfig() {
	    SERVER_CONFIG = PropertiesBootstrap.loadServerConfigFromLocal();
	}

第四步遍历容器中的实例化对象,找出需要暴露服务的服务端对象,并进行修饰添加额外的属性,最重要的是进行服务的注册(该步骤仅将服务URL添加到PROVIDER_URL_SET集合中,为后续注册中心进行真正的服务注册做准备)。

	for (String beanName : beanMap.keySet()) {
	    Object bean = beanMap.get(beanName);
	    EasyRpcService easyRpcService = bean.getClass().getAnnotation(EasyRpcService.class);
	    ServiceWrapper dataServiceServiceWrapper = new ServiceWrapper(bean, easyRpcService.group());
	    dataServiceServiceWrapper.setServiceToken(easyRpcService.serviceToken());
	    dataServiceServiceWrapper.setLimit(easyRpcService.limit());
	    dataServiceServiceWrapper.setWeight(easyRpcService.weight());
	    // 服务注册
	    server.registyService(dataServiceServiceWrapper);
	    LOGGER.info(">>>>>>>>>>>>>>> [easy-rpc] {} export success! >>>>>>>>>>>>>>> ", beanName);
	}

第五步是注册一个服务器关闭的钩子函数,在服务器JVM进程关闭时调用。

	ServerShutdownHook.registryShutdownHook();

第六步是启动服务器。

	server.startServerApplication();

三、关键函数

1.server.registyService()方法

该方法是将包装好的服务类添加到PROVIDER_URL_SET集合中,并且在此之前使用SPI的方式根据配置实例化对应的注册中心(本文采用的是Zookeeper)。

    public void registyService(ServiceWrapper serviceWrapper) {
        // 具体的实现类bean
        Object serviceBean = serviceWrapper.getServiceBean();
        if (serviceBean.getClass().getInterfaces().length == 0) {
            throw new RuntimeException("service must had interfaces!");
        }
        // 获得所有接口
        Class<?>[] classes = serviceBean.getClass().getInterfaces();
        if (classes.length > 1) {
            throw new RuntimeException("service must only had one interfaces!");
        }
        // 实例化注册中心
        if (REGISTRY_SERVICE == null) {
            try {
                // 通过SPI的方式默认加载ZookeeperRegister
                EXTENSION_LOADER.loadExtension(RegistryService.class);
                // EXTENSION_LOADER_CLASS_CACHE中存放接口的实现类,一个接口多个实现类
                // key:com.x.x.x.RegistryService  value: <zookeeper, xxx.class>, <nacos, xxx.class>
                Map<String, Class<?>> registryClassMap = EXTENSION_LOADER_CLASS_CACHE.get(RegistryService.class.getName());
                // 根据服务器配置得到具体的注册实现类 zookeeper
                Class<?> registryClass = registryClassMap.get(SERVER_CONFIG.getRegisterType());
                // 实例化->ZookeeperRegistry
                REGISTRY_SERVICE = (AbstractRegister) registryClass.newInstance();
            } catch (Exception e) {
                throw new RuntimeException("registryServiceType unKnow,error is ", e);
            }
        }
        // 默认选择该对象的第一个实现接口
        Class<?> interfaceClass = classes[0];
        // 具体提供服务的类放入map中 com.shaogezhu.easy.rpc.interfaces.DataService -> {DataServiceImpl@5435}
        PROVIDER_CLASS_MAP.put(interfaceClass.getName(), serviceBean);
        URL url = new URL();
        url.setServiceName(interfaceClass.getName());
        url.setApplicationName(SERVER_CONFIG.getApplicationName());
        url.addParameter("host", CommonUtil.getIpAddress());
        url.addParameter("port", String.valueOf(SERVER_CONFIG.getPort()));
        url.addParameter("group", String.valueOf(serviceWrapper.getGroup()));
        url.addParameter("limit", String.valueOf(serviceWrapper.getLimit()));
        url.addParameter("weight", String.valueOf(serviceWrapper.getWeight()));
        // 服务提供者的ip:port,以及服务提供者的权重,方便后续添加到注册中心
        PROVIDER_URL_SET.add(url);
        if (serviceWrapper.getLimit() > 0) {
            SERVER_SERVICE_SEMAPHORE_MAP.put(interfaceClass.getName(), new ServerServiceSemaphoreWrapper(serviceWrapper.getLimit()));
        }
        if (CommonUtil.isNotEmpty(serviceWrapper.getServiceToken())) {
            PROVIDER_SERVICE_WRAPPER_MAP.put(interfaceClass.getName(), serviceWrapper);
        }
    }

2.server.startServerApplication()方法

在该方法中,配置了Netty服务端,并对过滤链、监听器和序列化工具进行初始化(同样是采用SPI的机制进行个性化加载实例),这其中需要关注的是SERVER_CHANNEL_DISPATCHER这个类,在Netty链式处理中的ServerHandler()会将请求进行封装并添加到SERVER_CHANNEL_DISPATCHER中的阻塞队列中(详见源码ServerHandler.java),在SERVER_CHANNEL_DISPATCHER声明了一个固定大小的线程池和一个阻塞队列,在SERVER_CHANNEL_DISPATCHER中会执行真正的服务调用以及服务调用结果的发送(详见源码ServerChannelDispatcher.java)。

    public void startServerApplication() throws InterruptedException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup);
        bootstrap.channel(NioServerSocketChannel.class);
        // 关闭Nagle算法,该算法要求一个TCP连接上最多只能有一个未被确认的小分组,在该小分组的确认到来之前,不能发送其他小分组。
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        // backlog 用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。
        // 服务器TCP内核 内维护了两个队列,称为A(未连接队列)和B(已连接队列)
        // 如果A+B的长度大于Backlog时,新的连接就会被TCP内核拒绝掉。
        bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
        // TCP发送缓冲区的容量上限
        bootstrap.option(ChannelOption.SO_SNDBUF, 16 * 1024)
                // TCP接受缓冲区的容量上限
                .option(ChannelOption.SO_RCVBUF, 16 * 1024)
                // 当设置为true的时候,TCP会实现监控连接是否有效,当连接处于空闲状态的时候,
                // 超过了2个小时,本地的TCP实现会发送一个数据包给远程的 socket,
                // 如果远程没有发回响应,TCP会持续尝试11分钟,直到响应为止,如果在12分钟的时候还没响应,TCP尝试关闭socket连接。
                .option(ChannelOption.SO_KEEPALIVE, true);

        //服务端采用单一长连接的模式,这里所支持的最大连接数和机器本身的性能有关
        //连接防护的handler应该绑定在Main-Reactor上
        bootstrap.handler(new MaxConnectionLimitHandler(SERVER_CONFIG.getMaxConnections()));
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                // 分隔符
                ByteBuf delimiter = Unpooled.copiedBuffer(DEFAULT_DECODE_CHAR.getBytes());
                // 解决半包粘包问题
                ch.pipeline().addLast(new DelimiterBasedFrameDecoder(SERVER_CONFIG.getMaxServerRequestData(), delimiter));
                ch.pipeline().addLast(new RpcEncoder());
                ch.pipeline().addLast(new RpcDecoder());
                // 拿到该通道的上下文和协议体添加到分发器中
                ch.pipeline().addLast(new ServerHandler());
            }
        });

        //初始化监听器
        RpcListenerLoader rpcListenerLoader = new RpcListenerLoader();
        rpcListenerLoader.init();

        //初始化序列化器
        String serverSerialize = SERVER_CONFIG.getServerSerialize();
        EXTENSION_LOADER.loadExtension(SerializeFactory.class);
        LinkedHashMap<String, Class<?>> serializeMap = EXTENSION_LOADER_CLASS_CACHE.get(SerializeFactory.class.getName());
        Class<?> serializeClass = serializeMap.get(serverSerialize);
        if (serializeClass == null) {
            throw new RuntimeException("no match serializeClass for " + serverSerialize);
        }
        SERVER_SERIALIZE_FACTORY = (SerializeFactory) serializeClass.newInstance();


        //初始化过滤链
        ServerBeforeFilterChain serverBeforeFilterChain = new ServerBeforeFilterChain();
        ServerAfterFilterChain serverAfterFilterChain = new ServerAfterFilterChain();
        EXTENSION_LOADER.loadExtension(ServerFilter.class);
        LinkedHashMap<String, Class<?>> filterChainMap = EXTENSION_LOADER_CLASS_CACHE.get(ServerFilter.class.getName());
        for (Map.Entry<String, Class<?>> filterChainEntry : filterChainMap.entrySet()) {
            String filterChainKey = filterChainEntry.getKey();
            Class<?> filterChainImpl = filterChainEntry.getValue();
            if (filterChainImpl == null) {
                throw new RuntimeException("no match filterChainImpl for " + filterChainKey);
            }
            SPI spi = filterChainImpl.getDeclaredAnnotation(SPI.class);
            if (spi != null && "before".equalsIgnoreCase(spi.value())) {
                serverBeforeFilterChain.addServerFilter((ServerFilter) filterChainImpl.newInstance());
            } else if (spi != null && "after".equalsIgnoreCase(spi.value())) {
                serverAfterFilterChain.addServerFilter((ServerFilter) filterChainImpl.newInstance());
            }
        }
        SERVER_BEFORE_FILTER_CHAIN = serverBeforeFilterChain;
        SERVER_AFTER_FILTER_CHAIN = serverAfterFilterChain;

        // 初始化请求分发器
        SERVER_CHANNEL_DISPATCHER.init(SERVER_CONFIG.getServerQueueSize(), SERVER_CONFIG.getServerBizThreadNums());
        // 开始接收客户端消息并启动额外线程进行处理
        SERVER_CHANNEL_DISPATCHER.startDataConsume();

        // 暴露服务端url
        this.batchExportUrl();
        bootstrap.bind(SERVER_CONFIG.getPort()).sync();
    }

3.server.batchExportUrl()方法

server.registyService()方法完成PROVIDER_URL_SET集合的设置后,调用REGISTRY_SERVICE注册中心实例化对象进行真正的服务注册。

    public void batchExportUrl() {
        Thread task = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (URL url : PROVIDER_URL_SET) {
                    // 进行服务注册
                    REGISTRY_SERVICE.register(url);
                }
            }
        });
        task.start();
    }
    public void register(URL url) {
        if (!zkClient.existNode(ROOT)) {
            zkClient.createPersistentData(ROOT, "");
        }
        //name;interface;ip:port;time;weight;group
        //rpc-provider;com.shaogezhu.easy.rpc.interfaces.DataService;192.168.174.1:8010;1702881112921;100;data-group
        String urlStr = URL.buildProviderUrlStr(url);
        if (zkClient.existNode(getProviderPath(url))) {
            zkClient.deleteNode(getProviderPath(url));
        }
        // path->easy-rpc/name/provider/ip:port, data
        zkClient.createTemporaryData(getProviderPath(url), urlStr);
        super.register(url);
    }

注册成功后的服务在Zookeeper上的结果如下图所示:
在这里插入图片描述
其中可以看到节点内所包含的服务配置信息。

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值