微服务架构-高性能Netty服务器-066:基于Netty+Zookeeper手写Dubbo框架(下)

1 回顾上节课手写Dubbo环境思路

课程内容:
1.基于Netty+Zookeeper实现服务注册
2.基于策略模式手写Dubbo负载均衡器
3.基于Netty+Zookeeper实现服务发现
4.完全百分百实现Dubborpc远程调用框架

2 Dubbo原理分析之Dubbo协议

发布的接口注册到zk节点上,key为接口路径地址,value为连接地址;
连接地址不一致(例如端口号改变)会和之前的地址一起形成集群;
dubbo一个接口中存在多个方法,也只会生成一个连接
eg:客户端使用的是前部分的ip端口和接口地址,后部分给dubbo-admin解析配置。dubbo://10.16.10.87:20880/com.mayikt.api.service.UserService?anyhost=true&application=mayikt-provider&dubbo=2.5.3&interface=com.mayikt.api.service.UserService&methods=delUser,getUser&pid=17608&side=provider×tamp=1612427752096
DubboAdmin部署流程
将dubbo-admin放入到tomcat webapps目录下,修改dubbo-admin/WEB-INF/dubbo.properties zk连接地址,运行tomcat。
访问http://127.0.0.1:8080/dubbo-admin/,账号root,密码root123。
在这里插入图片描述

3 自定义RPC注解发布Dubbo服务接口

手写Dubbo核心思想与代码
mayikt-netty-dubbo-server — dubbo核心的服务
netty-dubbo-common —工具类
member-service-producer-api – 会员服务接口
member-service-producer-impl –会员服务接口实现
order-consumer-impl –订单消费者

netty-dubbo-common

/**
 * 相当于dubbo服务注册,替代xml
 */
@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RpcAnnotation {
    Class value();
}

member-service-producer-api

public interface MemberService {

    /**
     * 公共的api接口
     *
     * @param userId
     * @return
     */
    String getUser(Long userId);

    int delUser(Long userId);
}

member-service-producer-impl

@RpcAnnotation(MemberService.class)
public class MemberServiceImpl implements MemberService {
    @Override
    public String getUser(Long userId) {
        return "mayikt";
    }

    @Override
    public int delUser(Long userId) {
        return 666;
    }
}

4 将本地服务信息注册到zk上

Dubbo实现思想流程

  1. 把需要发布的服务(会员服务)注册到zk,注册成功后启动netty;
  2. 订单服务去zk找到地址再通过本地实现rpc调用;
  3. netty服务端接收到参数再通过反射执行方法。

服务端

public class MayiktRpcServer {

    private ServiceRegistration serviceRegistration;
    private String host;
    private int port;

    private Map<String, Object> handlerMap = new HashMap<String, Object>();

    public MayiktRpcServer(String host, int port) {
        this.host = host;
        this.port = port;
        serviceRegistration = new ServiceRegistrationImpl();
    }

    /**
     * 核心流程
     * bind() 将本地服务注册到zk
     * 启动netty服务器端实现监听
     */

    public void bind(Object obj) {

        // 获取类上面是否有加上rpc注解
        RpcAnnotation declaredAnnotation = obj.getClass().getDeclaredAnnotation(RpcAnnotation.class);
        if (declaredAnnotation == null) {
            // 抛出异常
            return;
        }
        // 获取到service_name
        String serviceName = declaredAnnotation.value().getName();
        String serviceAddress = "mayikt://" + host + ":" + port;
        serviceRegistration.register(serviceName, serviceAddress);

        // obj已经实例化出了对象,存map集合后续handler解析出地址直接拿对象不用再new一个
        handlerMap.put(serviceName, obj);
    }

    private void nettyStart() {
        /**
         *  客户端创建两个线程池组分别为 boss线程组和工作线程组
         */
        // 用于接受客户端连接的请求 (并没有处理请求)
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        // 用于处理客户端连接的读写操作
        NioEventLoopGroup workGroup = new NioEventLoopGroup();
        // 用于创建我们的ServerBootstrap
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        // 监听handler
                        socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                        socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                        socketChannel.pipeline().addLast(new DubboServerHandler(handlerMap));
                    }
                });
        //  绑定我们的端口号码
        try {
            // 绑定端口号,同步等待成功
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("服务器启动成功:" + port);
            // 等待服务器监听端口
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            // 优雅的关闭连接
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
    public void start(Object object){
        // 将服务注册到zk上
        bind(object);
        // 启动netty服务
        nettyStart();
    }
}

5 生产者启动Netty服务器端监听消息

消费者调用生产者需要一个非常详细的地址:
mayikt://192.168.x.x:8080/com.mayikt.service.userService.getUser 参数、类型、返回值

传递的消息对象

@Data
@AllArgsConstructor
public class RpcRequest implements Serializable {
    private static final long SerialVersionUID = 1L;
    // mayikt://192.168.1.1:8080/com.mayikt.api.service.UserService.getUser Long 10 序列化成对象
    /**
     * 类的className
     */
    private String className;
    /**
     * 方法名称
     */
    private String methodName;
    /**
     * 参数类型
     */
    Class<?> parameterTypes[];
    /**
     * 参数value
     */
    Object paramsValue[];

    @Override
    public String toString() {
        return className + "," + methodName + "," + parameterTypes + paramsValue;
    }
}
public class DubboServerHandler extends ChannelInboundHandlerAdapter {

    private Map<String, Object> handlerMap;

    public DubboServerHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    /**
     * 服务端监听客户端发送消息
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 获取请求对象
        RpcRequest rpcRequest = (RpcRequest) msg;
        System.out.println("服务器端获取到消息:" + rpcRequest.toString());
        // 采用反射机制执行实现类的方法
        if (rpcRequest == null) {
            return;
        }
        String serviceName = rpcRequest.getClassName();
        Object objectImpl = handlerMap.get(serviceName);
        if (objectImpl == null) {
            return;
        }
        // 使用反射机制执行目标方法
        Method method = objectImpl.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParameterTypes());
        Object result = method.invoke(objectImpl, rpcRequest.getParamsValue());
        // 返回结果
        System.out.println("服务器端响应给客户端:" + result);
        ctx.writeAndFlush(result);
    }
}

6 消费端从zk上获取注册地址信息

mayikt-netty-dubbo-server

public interface ServiceDiscover {

    /**
     * 地址可能存在多个
     * @param serviceName
     * @return
     */
    List<String> getDiscover(String serviceName);
}
public class ServiceDiscoverImpl implements ServiceDiscover {

    /**
     * zk连接地址
     */
    private final String zkServers = "127.0.0.1";
    /**
     * 会话时间
     */
    private final int connectionTimeout = 5000;
    /***
     * zkClient
     */
    private ZkClient zkClient;

    private String rootNamePath = "/mayikt_rpc";

    public ServiceDiscoverImpl() {
        zkClient = new ZkClient(zkServers, connectionTimeout);
    }

    @Override
    public List<String> getDiscover(String serviceName) {
        List<String> children = zkClient.getChildren(rootNamePath + "/" + serviceName + "/providers");
        return children;
    }
    // 负载均衡器 轮询、权重、取模、随机、哈希

}

7 基于策略模式实现负载均衡轮询机制

public interface LoadBalance {

    /**
     * 实现Dubbo 负载均衡器 轮询、随机、一致性hash
     * @param repos
     * @return
     */
    String select(List<String> repos);
}
public class LoopLoadBalance implements LoadBalance {

    private int index;

    @Override
    public synchronized String select(List<String> repos) {
        if (index > repos.size()) {
            index = 0;
        }
        String value = repos.get(index++);
        System.out.println("value:" + value);
        return value;
    }
}
public class RandomLoadBalance implements LoadBalance {
    @Override
    public String select(List<String> repos) {
        String value = repos.get(new Random().nextInt(repos.size()));
        System.out.println("value:" + value);
        return value;
    }
}

8 消费端启动Netty发送消息给服务器端

public class RpcClientProxy {
    public <T> T create(final Class<T> interfaceClass) {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 使用代理类拼接地址
                ServiceDiscover serviceDiscover = new ServiceDiscoverImpl();
                String serviceName = interfaceClass.getName();
                List<String> discover = serviceDiscover.getDiscover(serviceName);
                // 默认使用轮询负载均衡器
                LoopLoadBalance loopLoadBalance = new LoopLoadBalance();
                // 获取到地址 zk存的值 mayikt://192.168.x.x:8080
                String select = URLDecoder.decode(loopLoadBalance.select(discover));
                String[] split = select.split(":");
                String host = split[1].replace("//","");
                String port = split[2];
                // 封装具体调用的参数
                RpcRequest rpcRequest = new RpcRequest(serviceName, method.getName(), method.getParameterTypes(), args);
                // 启动netty客户端发送消息
                return sendMsg(host, Integer.parseInt(port), rpcRequest);
            }
        });
    }

    public Object sendMsg(String host, int port, final RpcRequest rpcRequest) {
        final DubboClientHandler dubboClientHandler = new DubboClientHandler();
        //创建nioEventLoopGroup
        NioEventLoopGroup group = new NioEventLoopGroup();
        final Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group).channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(host, port))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                        ch.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
                        ch.pipeline().addLast(dubboClientHandler);
                    }
                });
        try {
            // 发起同步连接
            ChannelFuture sync = bootstrap.connect().sync();
            // 客户端发送我们的对象
            System.out.println("生产者发送消息:" + rpcRequest.toString());
            sync.channel().writeAndFlush(rpcRequest);
            sync.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();

        } finally {
            group.shutdownGracefully();
        }
        return dubboClientHandler.getResponse();

    }
}
public class DubboClientHandler extends ChannelInboundHandlerAdapter {
    private Object response;

    public Object getResponse() {
        return response;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        this.response = msg;
        // 客户端获取服务器端响应之后应该主动关闭连接
        System.out.println("客户端获取到服务器端响应:" + this.response);
        ctx.close();
    }
}

9 生产与消费端解决发送消息序列化问题

使用marshalling实现编码器

public final class MarshallingCodeCFactory {

    /**
     * 创建Jboss Marshalling解码器MarshallingDecoder
     * @return MarshallingDecoder
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
        //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        //创建了MarshallingConfiguration对象,配置了版本号为5
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //根据marshallerFactory和configuration创建provider
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
        MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
        return decoder;
    }

    /**
     * 创建Jboss Marshalling编码器MarshallingEncoder
     * @return MarshallingEncoder
     */
    public static MarshallingEncoder buildMarshallingEncoder() {
        final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
        //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
        MarshallingEncoder encoder = new MarshallingEncoder(provider);
        return encoder;
    }
}

引入依赖

<dependency>
    <groupId>org.jboss.marshalling</groupId>
    <artifactId>jboss-marshalling</artifactId>
    <version>1.4.10.Final</version>
</dependency>
<dependency>
    <groupId>org.jboss.marshalling</groupId>
    <artifactId>jboss-marshalling-serial</artifactId>
    <version>1.4.10.Final</version>
</dependency>

Netty服务端/客户端引入编码器

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

10 生产者核心代码启动断点调试分析

public class MemberProducer {
    public static void main(String[] args) {
        new MayiktRpcServer("127.0.0.1",8080).start(new MemberServiceImpl());
    }
}

断点调试结果:
在这里插入图片描述

11 调试消费端发送消息给生产者端

public class Consumer {
    public static void main(String[] args) {
        // 使用代理模式生成代理类
        MemberService memberService = new RpcClientProxy().create(MemberService.class);
        String user = memberService.getUser(10L);
        System.out.println("result:" + user);
        int delUser = memberService.delUser(1L);
        System.out.println("delUser:" + delUser);
    }
}

断点调试:
在这里插入图片描述
备注:断点调试服务端收到的消息有bug,服务器端直接运行就是正常情况。
在这里插入图片描述

运行结果:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值