自研RPC实现

跟孙哥学java

孙哥主页

明确需求

  1. 提供一个RPC的服务集群,提供RPC服务
  2. 提供客户端进行服务端调用
  3. 服务治理 (注册中心)zookeeper

技术难点

  1. client和服务端不在同一个虚拟机中-->怎么透明的调用远端RPC的服务
    1. 也就是客户端像调用本地方法那样去调用远端的服务方法
  2. 自动发现RPC集群中的节点(注册中心--->服务发现)
  3. 负载均衡
  4. 集群容错--->获取的那个服务,拿到地址之后正好出现问题,再去访问就出现问题了

技术选型

  1. 客户端client透明的去调用远端的RPC服务
    1. 远端调用,跨进程--->怎么解决?
    2. 网络通信-->BIO,NIO,Mina,netty,Web服务
    3. 传输数据,数据格式-->协议 Http1,Http2,TCP(自己封装)
    4. 序列化 Protobuf, Thrift, JDK, JSON
    5. 透明调用-->代理,JDK动态代理,CJlib动态代理
  2. 自动发现RPC集群中的节点-->服务发现

zookeeper ,java,Curator

  1. 负载均衡

客户端的负载均衡-->拉取数据,从zk查询 ,监听数据的变换-> 通过curator,zkclient 获取数据之后--->负载均衡算法 -->访问某一个服务节点 rondrobin,random,weight

  1. 集群容错

failFast--> 拉取的服务节点是坏的--->直接抛出异常 failOver-->去拉取集群服务中其他的节点,重试-->(延迟队列,定时任务) 时间轮

概念设计(抽象)

1.Transport 2.Protocol 3.Serializar 4.代理 5.Cluster 6.负载均衡 7.集群容错(故障容错)

编码实现

新建一个maven项目

这里选择jdk17

引入所需要的依赖

xml 复制代码
 <dependencies>
        <!--netty->做网络通信-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.45.Final</version>
        </dependency>
<!--        curator:zk的客户端-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>5.2.1</version>
        </dependency>
<!--hessina序列化-->
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.38</version>
        </dependency>
        <!--hutool工具包-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.10</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>


    </dependencies>

序列化设计

  1. Hessian
  2. JDK
  3. JSON
  4. Thrift
  5. Protobuf

为了屏蔽实现的差异,已于程序的扩展--->让具体的实现继承共同的接口---Serializar Serializar 接口

java 复制代码
public interface Serializar {
   //序列化 Protocol--->二进制
   byte[] serializar(Protocol protocol) throws Exception;
   //反序列化 二进制-->Protocol
   Protocol deserializar(byte[] bytes) throws Exception;
}

具体实现类: 1.Hessian序列化

java 复制代码
public class HessianSerializar implements Serializar {
    @Override
    public byte[] serializar(Protocol protocol) throws Exception {
        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        Hessian2Output hessian2Output=new Hessian2Output(outputStream);
        hessian2Output.writeObject(protocol);
        hessian2Output.flush();
        return outputStream.toByteArray();
    }

    @Override
    public Protocol deserializar(byte[] bytes) throws Exception {
        ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
        Hessian2Input hessian2Input=new Hessian2Input(inputStream);
        Object o = hessian2Input.readObject();
        return (Protocol) o;
    }
}

2.JDK序列化

java 复制代码
public class JDKSerializar implements Serializar {
    @Override
    public byte[] serializar(Protocol protocol) throws Exception {
        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(protocol);
        return outputStream.toByteArray();
    }

    @Override
    public Protocol deserializar(byte[] bytes) throws Exception {
        ByteArrayInputStream inputStream=new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
        Object o = objectInputStream.readObject();
        return (Protocol) o;
    }
}

3.JSON序列化

java 复制代码
public class JSONSerializar implements Serializar {
    @Override
    public byte[] serializar(Protocol protocol) throws Exception {

        String jsonStr = JSONUtil.toJsonStr(protocol);
        return jsonStr.getBytes(Charset.defaultCharset());
    }

    @Override
    public Protocol deserializar(byte[] bytes) throws Exception {
        String json = new String(bytes);
        return JSONUtil.toBean(json, Protocol.class);
    }
}

RPC通信的协议Protocol

  1. 请求(对象,拿个方法,参数是什么,实参是什么)

    MethodInvokeData--->请求对象 Class targetInterface--->访问的接口 String methodName--->方法名称 Class<?>[] parmeterType--->方法参数 Object[] args--->请求参数

  2. 响应

Result--->响应结果 Object resultValue--->实际需要的响应结果 Exception exception-->异常 String state -->响应状态 200:通过 400:异常 :::info 如果请求MethodInvokeData是一个类,响应Result又是一个类,我们就要定义两套协议,请求协议和响应协议,因此---> 使用一个公共接口让 请求类和继承类都继承共同的接口。 :::

进一步抽象Protocol协议 编解码器:

  1. 魔术/幻术--这个可以自己定义--我定义的是HURPC
  2. 协议版本--一般用一个字节表示,这里使用 byte PROTOCOL_VERSION=1
  3. 数据的长度--用来记录数据的长度
  4. 实际传输的数据--MethodInvokeData,Result

编解码器

定义好了编解码的规则,就来实现RPCMessageToMessageCodec,让它继承MessageToMessageCodec<ByteBuf, Protocol>,表示 encode---> Protocol--> ByteBuf decode---> ByteBuf--> Protocol

java 复制代码
@Slf4j
public class RPCMessageToMessageCodec extends MessageToMessageCodec<ByteBuf, Protocol> {
    //序列化的类
   private Serializar serializar=new HessianSerializar();
   @Override
   protected void encode(ChannelHandlerContext ctx, Protocol protocol, List<Object> out) throws Exception {
      System.out.println("编码"+protocol);

      ByteBufAllocator allocator=ctx.alloc();
      ByteBuf buffer = allocator.buffer();
      byte[] bytes = serializar.serializar(protocol);
      //1.魔术 --MYRPC -五个字节
      buffer.writeBytes(Protocol.MAGIC_NUM.getBytes(StandardCharsets.UTF_8));
      //2.版本--一个字节 byte
      buffer.writeByte(Protocol.PROTOCOL_VERSION);
      //3.Protocol长度
      buffer.writeInt(bytes.length);
      //4.Protocol
      buffer.writeBytes(bytes);
      out.add(buffer);
   }

   @Override
   protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
      CharSequence charSequence = msg.readCharSequence(5, StandardCharsets.UTF_8);
      System.out.println("解码开始");
      if(!Protocol.MAGIC_NUM.equals(charSequence.toString())){
         //魔术不匹配
         throw new RuntimeException("MAGIC_NUM ERROR");
      }
      byte protocolVersion = msg.readByte();
      if(Protocol.PROTOCOL_VERSION!=protocolVersion){
         //协议版本不一致
         throw new RuntimeException("PROTOCOL_VERSION NOT NORMAL");
      }
      int length = msg.readInt();
      byte[] bytes=new byte[length];
      msg.readBytes(bytes);
      //反序列化
      Protocol protocol = serializar.deserializar(bytes);
      System.out.println("解码"+protocol);
      out.add(protocol);

   }
}

服务端设计

:::info 服务端启动-->开启ServerBootSrap--->服务注册-->将当前服务开发的Service注册到zookeeper-->等待客户端访问(满足我们定义的协议Protocol) :::

java 复制代码
/*
  1. 构建netty服务端
  2. 引入注册中心
  3. 引入所有的业务对象
  4. 通过反射进行调用
 */
@ToString
public class RPCServiceProvider {
    //端口号
    private int port;
    private EventLoopGroup eventLoopGroupBoss;
    private EventLoopGroup eventLoopGroupWorker;
    //Netty的编解码器 内置Handler通过这个线程组服务
    private EventLoopGroup eventLoopGroupHandler;
    private EventLoopGroup eventLoopGroupService;
    private int workerThreads;
    private int handlerThreads;
    private int serviceThreads;
    private Registry registry;
    private ServerBootstrap serverBootstrap;

    private Map<String,Object> exposeBeans;
    private volatile boolean isStarted=false;
    //默认8080,一个工作线程,一个handler线程,一个service线程
    public RPCServiceProvider(Registry registry, Map<String, Object> exposeBeans) {
        this(8080, 1, 1, 1, registry, exposeBeans);
    }

    //一个工作线程,一个handler线程,一个service线程  -自定义端口
    public RPCServiceProvider(int port, Registry registry, Map<String, Object> exposeBeans) {
        this(port, 1, 1, 1, registry, exposeBeans);
    }

    public RPCServiceProvider(int port, int workerThreads, int handlerThreads, int serviceThreads, Registry registry, Map<String, Object> exposeBeans) {
        this.port = port;
        this.workerThreads = workerThreads;
        this.handlerThreads = handlerThreads;
        this.serviceThreads = serviceThreads;

        this.eventLoopGroupBoss = new NioEventLoopGroup(1);
        this.eventLoopGroupWorker = new NioEventLoopGroup(this.workerThreads);
        this.eventLoopGroupHandler = new DefaultEventLoopGroup(this.handlerThreads);
        this.eventLoopGroupService = new DefaultEventLoopGroup(this.serviceThreads);

        this.registry = registry;
        this.serverBootstrap = new ServerBootstrap();
        this.exposeBeans = exposeBeans;

    }
    //1.开启服务
    public void startServer(){
        if(isStarted){
            throw new RuntimeException(" server is already started...");
        }
        System.out.println("服务启动");
        serverBootstrap.group(new NioEventLoopGroup());
        serverBootstrap.channel(NioServerSocketChannel.class);
        System.out.println(this);

     serverBootstrap.childHandler(new RPCServiceProviderInitialize(this.eventLoopGroupHandler,this.eventLoopGroupService,exposeBeans));
        ChannelFuture channelFuture = serverBootstrap.bind(port);
        channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if(future.isSuccess()){
                    //2.服务注册功能
                    registerServices(InetAddress.getLocalHost().getHostAddress(),port,exposeBeans,registry);
                    isStarted=true;
                    //监听关闭
                    Channel channel = channelFuture.channel();
                    ChannelFuture closeFuture = channel.closeFuture();
                    closeFuture.addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture future) throws Exception {
                            if(future.isSuccess()){
                                stopService();
                            }
                        }
                    });
                }
            }
        });
        //异常关闭
        Runtime.getRuntime().addShutdownHook(new Thread(()->{
            stopService();
        }));
    }
    //关闭服务,释放资源

    private void stopService() {
        eventLoopGroupBoss.shutdownGracefully();
        eventLoopGroupWorker.shutdownGracefully();
        eventLoopGroupHandler.shutdownGracefully();
        eventLoopGroupService.shutdownGracefully();

    }

    private void registerServices(String hostAddress, int port, Map<String, Object> exposeBeans, Registry registry)  {
        Set<String> keySet = exposeBeans.keySet();
        HostAndPort hostAndPort=new HostAndPort(hostAddress,port);
        for (String key : keySet) {
            try {
                registry.registryService(key,hostAndPort);
            } catch (Exception e) {
                throw new RuntimeException("register is error");
            }
        }

    }
}
java 复制代码
@Slf4j
public class RPCServiceProviderInitialize extends ChannelInitializer<NioSocketChannel> {
    private EventLoopGroup eventLoopGroupHandler;
    private EventLoopGroup eventLoopGroupService;
    private Map<String, Object> exposeBean;

    public RPCServiceProviderInitialize(EventLoopGroup eventLoopGroupHandler, EventLoopGroup eventLoopGroupService,Map<String, Object> exposeBean) {
        this.eventLoopGroupHandler = eventLoopGroupHandler;
        this.eventLoopGroupService = eventLoopGroupService;
        this.exposeBean=exposeBean;
    }

    private Result executeTargetObject(MethodInvokeData methodInvokeData, Map<String, Object> exposeBean) throws NoSuchMethodException {
        System.out.println("执行rpc方法");
        Class targetInterface = methodInvokeData.getTargetInterface();
        //获取接口实现方法-->通过接口名字
        Object nativeObj = exposeBean.get(targetInterface.getName());
        Method method = targetInterface.getDeclaredMethod(methodInvokeData.getMethodName(), methodInvokeData.getParameterType());
        //进行方法的调用
        Result result=new Result();

        try {
            Object ret = method.invoke(nativeObj, methodInvokeData.getArgs());
            result.setState(RPCServiceState.SUCCESS);
            result.setResultValue(ret);
            log.debug("成功调用result:{}",ret);

        } catch (Exception e) {
            log.error("异常",e);
           e.printStackTrace();
           result.setState(RPCServiceState.FAIL);
           result.setException(e);
        }
        return result;
    }

    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline(); // z
        //封帧  ---使用eventLoopGroupHandler
        pipeline.addLast(eventLoopGroupHandler,new LengthFieldBasedFrameDecoder(1024,6,4,0,0));
        //LoggingHandler--使用eventLoopGroupHandler
        pipeline.addLast(eventLoopGroupHandler,new LoggingHandler());
        //编解码器--使用eventLoopGroupService
        pipeline.addLast(eventLoopGroupService,new RPCMessageToMessageCodec());
        //RPC功能的调用-使用eventLoopGroupService
        pipeline.addLast(eventLoopGroupService,new ChannelInboundHandlerAdapter(){
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                //代表client提交的用于进行rpc调用的参数
                MethodInvokeData methodInvokeData=(MethodInvokeData)msg;
                Result result=executeTargetObject(methodInvokeData,exposeBean);
                ChannelFuture channelFuture = ctx.writeAndFlush(result);
                channelFuture.addListener(ChannelFutureListener.CLOSE);
                channelFuture.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);

            }
        });
    }
}

客户端设计

Transport设计

代表的是client对服务端 访问---基于网络访问 BIO NIO Netty Mina...

java 复制代码
public interface Transport {

   public Result invoke(HostAndPort hostAndPort, MethodInvokeData methodInvokeData) throws Exception;

   public void  close( );

}
java 复制代码
public class NettyTransport implements Transport {
    private Bootstrap bootstrap;
    private EventLoopGroup worker;
    private int workerThreads;

    public NettyTransport(int workerThreads) {
        this.workerThreads = workerThreads;
        worker=new NioEventLoopGroup(workerThreads);
        bootstrap=new Bootstrap();
        bootstrap.group(worker);
        bootstrap.channel(NioSocketChannel.class);

    }

    @Override
    public Result invoke(HostAndPort hostAndPort, MethodInvokeData methodInvokeData) throws Exception {
        RPCClientChannelInitializer rpcClientChannelInitializer=new RPCClientChannelInitializer(methodInvokeData);
        bootstrap.handler(rpcClientChannelInitializer);
        ChannelFuture channelFuture = bootstrap.connect(hostAndPort.getHostName(), hostAndPort.getPort()).sync();
        channelFuture.channel().closeFuture().sync();


        return rpcClientChannelInitializer.getResult();
    }


    @Override
    public void close() {
        worker.shutdownGracefully();

    }
}
java 复制代码
@Slf4j
public class RPCClientChannelInitializer extends ChannelInitializer<NioSocketChannel> {
   private MethodInvokeData methodInvokeData;
   private Result result=null;

   public Result getResult() {
      return result;
   }

   public RPCClientChannelInitializer(MethodInvokeData methodInvokeData) {
      this.methodInvokeData = methodInvokeData;
   }

   @Override
   protected void initChannel(NioSocketChannel ch) throws Exception {
      ChannelPipeline pipeline = ch.pipeline();
      pipeline.addLast(new LengthFieldBasedFrameDecoder(1024,6,4,0,0));
      pipeline.addLast(new RPCMessageToMessageCodec());
      pipeline.addLast(new LoggingHandler());
      pipeline.addLast(new ChannelInboundHandlerAdapter(){
         @Override
         public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            log.debug("接收到返回值了:{}",msg);
             result=(Result)msg;

         }

         @Override
         public void channelActive(ChannelHandlerContext ctx) throws Exception {
            log.debug("发送数据:{}",methodInvokeData);
            ChannelFuture channelFuture = ctx.writeAndFlush(methodInvokeData);
            channelFuture.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
         }
      });

   }
}

LoadBalancaer负载均衡

  1. 随机
  2. 轮询
  3. 一致性hash
  4. weigth

这里就实现了随机算法

java 复制代码
public class RandomLoadBalancer implements LoadBalancer {
    private Random random=new Random();
    @Override
    public HostAndPort select(List<HostAndPort> hostAndPorts) {
        if(hostAndPorts==null||hostAndPorts.size()==0){
            throw new RuntimeException("hostAndPorts set is null");
        }
        int index=random.nextInt(hostAndPorts.size());
        return hostAndPorts.get(index);
    }
}

Cluster[容错]

  1. 参数
  2. 返回值Result

Cluster的实现 考虑不同的容错方式,导致了集群的实现方式的区别 FailFast--->一旦访问RPC服务失败,直接抛出异常 FailOver--->一旦访问失败,会拉取集群中其他的服务进行访问

java 复制代码
@Slf4j
public class FailFastCluster implements Cluster {
    @Override
    public Result invoke(Registry registry, LoadBalancer loadBalancer, Transport transport, MethodInvokeData methodInvokeData) {
        List<HostAndPort> hostAndPorts = registry.receiveService(methodInvokeData.getTargetInterface().getName());
        HostAndPort hostAndPort = loadBalancer.select(hostAndPorts);
        Result result = null;
        try {
            result = transport.invoke(hostAndPort, methodInvokeData);

        } catch (Exception e) {
            log.error("调用RPC服务出现问题",e);
        }finally {
            transport.close();
        }
        return result;
    }
}
java 复制代码
@Slf4j
public class FailOverCluster implements Cluster {
    @Override
    public Result invoke(Registry registry, LoadBalancer loadBalancer, Transport transport, MethodInvokeData methodInvokeData) {
        List<HostAndPort> hostAndPorts = registry.receiveService(methodInvokeData.getTargetInterface().getName());
        HostAndPort hostAndPort = loadBalancer.select(hostAndPorts);
        log.debug("选择的hostAndPort is{}",hostAndPort.getHostName()+":"+hostAndPort.getPort());
        try {
            Result result = transport.invoke(hostAndPort, methodInvokeData);
            return result;
        } catch (Exception e) {
            log.error("集群调用产生错误,使用FailOverCluster 进行容错");
            hostAndPorts.remove(hostAndPort);
            if(hostAndPorts.size()==0){
                throw new RuntimeException("集群调用产生错误,没有可用的远程服务");
            }
            transport=new NettyTransport(1);
            return invoke(registry,loadBalancer,transport,methodInvokeData);
        }finally {
            transport.close();
        }
    }
}

实现,Service代理

代理的的实现的技术方案(静态代理 和动态代理) 动态代理 (JDK, cglib) Proxy.newProxyInstrance(ClassLoader,interfaces,InvocationHandler) 在InvocationHandler 调用RPC集群 ,发送数据---> MethodInvokeData --->获得响应--> Result

java 复制代码
public class JDKProxy implements InvocationHandler {
   private Class targetInterface;
   private Cluster cluster;
   private LoadBalancer loadBalancer;
   private Transport transport;
   private Registry registry;

   public void setRegistry(Registry registry) {
      this.registry = registry;
   }

   public void setCluster(Cluster cluster) {
      this.cluster = cluster;
   }

   public void setLoadBalancer(LoadBalancer loadBalancer) {
      this.loadBalancer = loadBalancer;
   }

   public void setTransport(Transport transport) {
      this.transport = transport;
   }



   public JDKProxy(Class targetInterface) {
      this.targetInterface = targetInterface;

   }

   public Object createProxy(){

      return Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), new Class[]{targetInterface}, this);
   }

   //通过远程调用rpc服务
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      MethodInvokeData methodInvokeData=new MethodInvokeData(targetInterface,method.getName(),method.getParameterTypes(),args);
      Result result = cluster.invoke(registry, loadBalancer, transport, methodInvokeData);
      if(result.getState()== RPCServiceState.SUCCESS){
         return result.getResultValue();
      }else {
         throw result.getException();
      }
   }
}

引入Spring

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="rPCServiceProvider" class="org.huy.RPCServiceProvider" init-method="startServer" destroy-method="stopService">
        <constructor-arg>
            <value>8080</value>
        </constructor-arg>
        <constructor-arg>
            <bean class="org.huy.registry.ZookeeperRegistry">
                <constructor-arg>
                    <value>127.0.0.1:2181</value>
                </constructor-arg>
            </bean>
        </constructor-arg>
        <constructor-arg>
            <map>
                <entry key="org.huy.service.UserService">
                    <bean class="org.huy.service.UserServiceImpl"/>
                </entry>
                <entry key="org.huy.service.OrderService">
                    <bean class="org.huy.service.OrderServiceImpl"/>
                </entry>
            </map>
        </constructor-arg>
    </bean>
</beans>

因为代理对象不是new出来的,我们使用spring工厂注入

java 复制代码
@Data
public class ProxyFactoryBean implements FactoryBean, InvocationHandler {

   private Class targetInterface;
   private Cluster cluster;
   private Registry registry;
   private LoadBalancer loadBalancer;
   private Transport transport;

   public ProxyFactoryBean(Class targetInterface) {
      this.targetInterface = targetInterface;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      MethodInvokeData methodInvokeData=new MethodInvokeData(targetInterface,method.getName(),method.getParameterTypes(),args);
      Result result = cluster.invoke(registry, loadBalancer, transport, methodInvokeData);
      if(result.getState()== RPCServiceState.FAIL){
         throw result.getException();
      }
      return result.getResultValue();
   }

   @Override
   public Object getObject() throws Exception {

      return  Proxy.newProxyInstance(targetInterface.getClassLoader(),new Class[]{targetInterface},this);
   }

   @Override
   public Class<?> getObjectType() {
      return targetInterface;
   }
}
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cluster" class="org.huy.cluster.FailFastCluster"/>
    <bean id="register" class="org.huy.registry.ZookeeperRegistry">
        <constructor-arg>
            <value>127.0.0.1:2181</value>
        </constructor-arg>
    </bean>
    <bean id="loadBalancer" class="org.huy.loadbalance.RandomLoadBalancer"/>
    <bean id="transport" class="org.huy.transport.NettyTransport">
        <constructor-arg>
            <value>1</value>
        </constructor-arg>
    </bean>

    <bean id="userService" class="org.huy.proxy.ProxyFactoryBean">
        <constructor-arg>
            <value>org.huy.service.UserService</value>
        </constructor-arg>
        <property name="cluster" ref="cluster"/>
        <property name="registry" ref="register"/>
        <property name="loadBalancer" ref="loadBalancer"/>
        <property name="transport" ref="transport"/>
    </bean>

</beans>
相关推荐
kirito学长-Java41 分钟前
springboot/ssm网上宠物店系统Java代码编写web宠物用品商城项目
java·spring boot·后端
海绵波波1071 小时前
flask后端开发(9):ORM模型外键+迁移ORM模型
后端·python·flask
余生H1 小时前
前端Python应用指南(二)深入Flask:理解Flask的应用结构与模块化设计
前端·后端·python·flask·全栈
AI人H哥会Java2 小时前
【Spring】基于XML的Spring容器配置——<bean>标签与属性解析
java·开发语言·spring boot·后端·架构
计算机学长felix2 小时前
基于SpringBoot的“大学生社团活动平台”的设计与实现(源码+数据库+文档+PPT)
数据库·spring boot·后端
sin22012 小时前
springboot数据校验报错
spring boot·后端·python
去哪儿技术沙龙2 小时前
去哪儿机票智能预警系统-雷达系统落地实践
后端
程序员大阳2 小时前
闲谭Scala(1)--简介
开发语言·后端·scala·特点·简介
直裾2 小时前
scala图书借阅系统完整代码
开发语言·后端·scala