netty的学习与rpc实践---使用spring框架进行整合

前文:构建Netty应用

项目地址: https://gitee.com/bigzibo/pp-netty-rpc

需求分析

  • netty在启动spring的时候自动启动
  • 需要编写代理类, 代理需要通信的接口
  • 需要使用rpc的接口在spring生成bean之前被代理
  • 通过服务或接口名与方法名找到服务端中对应的方法
  • 接口名对应的实现类在spring容器中做预处理

获取spring的上下文

通过实现ApplicationContextAware接口, 重写setApplicationContext就可以获得上下文,
spring会自动找到实现这个接口的类, 调用重写的这个方法

client

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.bootstrap = new Bootstrap();
        // 这里的group中的eventLoop只负责监听channel状态
        // 如果channel空闲则可以发送数据
        // 如果channel中有回传则接收数据
        this.group = new NioEventLoopGroup(1);
        this.bootstrap.group(group).
                channel(NioSocketChannel.class).
                option(ChannelOption.TCP_NODELAY, true).
                option(ChannelOption.SO_KEEPALIVE, true).
                handler(this);
        String zkAddress = applicationContext.getEnvironment().getProperty("zookeeper.address");
        // 生成zookeeper客户端
        ZookeeperClient zkClient = new ZookeeperClient(zkAddress);
        // manager是负责管理缓存和生成channel连接的类
        this.manager = new NettyChannelManager(this, zkClient);
        this.handler = new NettyClientHandler();
    }

客户端中还实现了InitializingBean接口, afterPropertiesSet方法会在初始化所有属性之后调用,

    @Override
    public void afterPropertiesSet(){
        // TODO do something...
        manager.refresh();
    }

这里我们调用了refresh方法, 即程序第一次刷新manager缓存的地方, 缓存中存放着服务与服务端地址, 地址与channel的关系

我们自定义了一个Sender接口, 该类实现了这个接口
send方法是客户端与服务端通信的起点, 获取channel并且获取返回结果

    @Override
    public Object send(Request request) throws InterruptedException {
        Channel channel = null;
        Class<?> clazz = null;
        try {
            clazz = Class.forName(request.getClassName());
            // 分布式下, 一个服务会部署到多台服务器, 所以这里用take轮询获得channel
            channel = manager.take(clazz.getSimpleName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (channel != null && channel.isActive()) {
            SynchronousQueue<Object> queue = this.handler.sendRequest(request, channel);
            // 等待结果返回, queue为空的时候, 这里会阻塞
            Object result = queue.take();
            return JSONArray.toJSONString(result);
        } else {
            Response res = new Response();
            res.setCode(502);
            assert clazz != null;
            res.setError("未正确连接到服务器.请检查相关配置信息! 服务名:" + clazz.getSimpleName());
            return JSONArray.toJSONString(res);
        }
    }

server

在服务端中, 获取上下文需要处理的事情除了client中初始化netty之外, 还需要通过找到注解并且反射得到服务名与服务实现类的数据结构

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        String address = applicationContext.getEnvironment().getProperty("remoteAddress");
        String zkAddress = applicationContext.getEnvironment().getProperty("zookeeper.address");
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(NettyService.class);
        for (Object serviceBean : beans.values()) {

            Class<?> clazz = serviceBean.getClass();

            Class<?>[] interfaces = clazz.getInterfaces();

            for (Class<?> inter : interfaces) {
            	// 找到@NettyService注解下的类, 获得其实现的接口
            	// 将接口全类名作为key, 存入map中
                String interfaceSimpleName = inter.getSimpleName();
                String interfaceName = inter.getName();
                // 这里注册入zookeeper
                ServiceRegistry serviceRegistry = new ServiceRegistry(zkAddress);
                // 使用简单类名在zookeeper中注册
                serviceRegistry.register(interfaceSimpleName, address);
                serviceMap.put(interfaceName, serviceBean);
            }
        }
        logger.info("已加载全部服务接口");
        // 新建一个serverWorker, 即前文中的netty初始化
        runner = new ServerWorker(address, serviceMap);
    }
   
    @Override
    public void afterPropertiesSet() throws Exception {
        runner.open();
    }

在前文的channelRead方法中, 我们只提起了一个名为handler的方法, 但是并未做代码展示, 因为那与netty无关, 所以放在这边作展示

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        Request request = JSON.parseObject(msg.toString(), Request.class);
        if ("heartBeat".equals(request.getMethodName())) {
            log.info("客户端心跳信息..."+ctx.channel().remoteAddress());
            return;
        }
        Response response = new Response();
        response.setRequestId(request.getId());
        try {
            Object res = this.handler(request);
            response.setData(res);
        } catch (Exception e) {
            response.setCode(-1);
            response.setError(e.getMessage());
            log.error("请求调用失败", e);
        }
        ctx.writeAndFlush(response);
    }

    private Object handler(Request request) throws Exception {
        String className = request.getClassName();
        // 根据className去拿反射的实现类
        // 前文中, serviceMap存的是全类名
        Object serviceBean = serviceMap.get(className);
        String methodName = request.getMethodName();
        Object[] parameters = request.getParameters();
        // 得到需要被反射的类, 方法, 参数后调用代理方法
        return MethodUtils.invokeMethod(serviceBean, methodName, parameters);
    }

Registrar

registrar包下的类, 功能就是用于注册beanDefinition, spring是依据beanDefinition来生成bean的
这边主要讲一下client的注册器, 因为server的与client的功能类似, 并且client多一个设置工厂来代理的过程.

public class NettyClientScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    private ResourceLoader resourceLoader;

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /**
    * @Description: 在spring生成bean之前
    */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    	// 获得的注册器传入扫描类, 并且新建扫描类
        NettyClientInterfaceScanner scanner = new NettyClientInterfaceScanner(registry);
        if (this.resourceLoader != null) {
            scanner.setResourceLoader(this.resourceLoader);
        }
        AnnotationAttributes annoAttrs =
                AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(NettyClientScan.class.getName()));
		// 这里得到包下的类工厂
        Class<? extends NettyFactoryBean> nettyFactoryBeanClass = annoAttrs.getClass("factoryBean");
        // 如果不是使用我们的自定义类工厂则设置使用
        if (!NettyFactoryBean.class.equals(nettyFactoryBeanClass)) {
            scanner.setNettyFactoryBean(BeanUtils.instantiateClass(nettyFactoryBeanClass));
        }

        List<String> basePackages = new ArrayList<>();
        // 通过basePackages的路径扫描包
        for (String pkg : annoAttrs.getStringArray("basePackages")) {
            if (StringUtils.hasText(pkg)) {
                basePackages.add(pkg);
            }
        }
        // 将路径下的包加入扫描
        scanner.setAnnotationClass(NettyClient.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
    }
}

Scanner类, 继承了spring 的扫描类

public class NettyClientInterfaceScanner extends ClassPathBeanDefinitionScanner {

    private NettyFactoryBean nettyFactoryBean = new NettyFactoryBean();

    private Class<? extends Annotation> annotationClass;

    public NettyClientInterfaceScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
	// 这里设置扫描哪个注解
    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        // 由于注册了过滤器, 所以这里扫描出来的bean都是带有注解的
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

        if (beanDefinitions.isEmpty()) {
            logger.warn("No netty interfaces was found in '"
                    + Arrays.toString(basePackages)
                    + "' package. Please check your configuration.");
        } else {
            // client需要被代理, 所以比server的多一步
            processBeanDefinitions(beanDefinitions);
        }

        return beanDefinitions;
    }

    // 注册过滤器, 过滤不含有annotationClass注解的类
    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
        }

        // exclude package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {

            String className = metadataReader.getClassMetadata()
                    .getClassName();
            return className.endsWith("package-info");
        });
    }

    /**
    * @Description: 将生成该类的bean工厂设置为NettyBeanFactory
    */
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {

        GenericBeanDefinition definition;

        for (BeanDefinitionHolder holder : beanDefinitions) {

            definition = (GenericBeanDefinition) holder.getBeanDefinition();
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            // bean信息中设置bean工厂为我们自定义的bean工厂
            definition.setBeanClass(this.nettyFactoryBean.getClass());

            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

    @Override
    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        return metadataReader.getClassMetadata().isInterface() && metadataReader.getClassMetadata().isIndependent();
    }

    public void setNettyFactoryBean(NettyFactoryBean instantiateClass) {
        this.nettyFactoryBean = instantiateClass != null ? instantiateClass : this.nettyFactoryBean;
    }
}

经过扫描和注册工厂之后, 经过扫描的接口都会被自定义的工厂生成

自定义的Bean工厂

public class NettyFactoryBean<T> implements FactoryBean<T> {
    private Class<T> nettyInterface;

    public NettyFactoryBean() {}

    public NettyFactoryBean(Class<T> nettyInterface) {
        this.nettyInterface = nettyInterface;
    }

    // 打了@NettyClient注解的类被Autowired的时候, 使用这里的getObj
    @Override
    public T getObject() throws Exception {
        // 返回代理, 代理类是我们的invoker
        return (T) Proxy.newProxyInstance(nettyInterface.getClassLoader(), new Class[]{nettyInterface}, NettyInterfaceInvoker.getInstance());
    }

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

    @Override
    public boolean isSingleton() {
        return true;
    }
}

在spring依赖注入的过程中, 我们将工厂设置为了自定义的工厂, 所以代理也使用了我们的代理类NettyInterfaceInvoker

代理类

NettyInterfaceInvoker
重点放在invoke方法上

    /**
    * @Description: 在FactoryBean中的getObject方法中的invoker类是该类, 所以该工厂生成的bean都会被该invoke方法代理
     * 这里完成了我们的基本模型, client调用service, 通过代理走netty通信调用服务端的实现类
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Request request = new Request();
        // 初始化request设置参数
        request.setClassName(method.getDeclaringClass().getName());
        request.setMethodName(method.getName());
        request.setParameters(args);
        request.setParameterTypes(method.getParameterTypes());
        // 线程特有的id, 多个线程访问一个channel的时候需要使用id判断
        request.setId(UUID.randomUUID().toString());
        // 通过netty去调用真正的服务, 获得回传
        Object result = sender.send(request);
        Class<?> returnType = method.getReturnType();
		// 如果返回的结果是502, 则说明服务器出现了一些问题
        Response response = JSON.parseObject(result.toString(), Response.class);
        if (response.getCode() == 502) {
            throw new Exception(response.getError());
        }
        // 根据method的返回值处理response
        if (returnType.isPrimitive() || String.class.isAssignableFrom(returnType)) {
            return response.getData();
        } else if (Collection.class.isAssignableFrom(returnType)) {
            return JSONArray.parseArray(response.getData().toString(), Object.class);
        } else if (Map.class.isAssignableFrom(returnType)) {
            return JSON.parseObject(response.getData().toString(), Map.class);
        } else {
            Object data = response.getData();
            return JSONObject.parseObject(data.toString(), returnType);
        }
    }

后记

以上则是有关spring的内容, 其实spring还是比较复杂的, 所以有些扫描注册的内容也都是网络上的模板套一套.
不过核心的内容就是实现一个自己的bean工厂, 在依赖注入service的时候, 代理类使用到的是自己的invoke
下文会讲一下manager和zookeeper, 因为我们需要将服务注册到zookeeper上, 所以获取服务和刷新服务缓存的内容需要整合到zookeeper去说明

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值