自研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>
相关推荐
用户4099322502123 分钟前
定时任务系统如何让你的Web应用自动完成那些烦人的重复工作?
后端·github·trae
Lx3523 分钟前
MapReduce性能调优:从理论到实践的经验总结
大数据·hadoop·后端
bobz96518 分钟前
小而精的 HRM 模型
后端
crossoverJie1 小时前
在多语言的分布式系统中如何传递 Trace 信息
分布式·后端·开源
用户848508146901 小时前
SurrealDB 快速上手教程
数据库·后端
用户6147493427741 小时前
JeecgBoot 项目理解与使用心得
后端
ZIQ2 小时前
单机线程池任务防丢设计与实现思路
后端
MaxHua2 小时前
我用 Java 飞算 AI 快速开发了一个音频转文字工具
后端
欧阳码农2 小时前
langgraph开发Deep Research智能体-项目搭建
前端·后端·langchain