2021-03-05

天气:阴雨

用Netty实现DubboRPC

这是参考尚硅谷课程的学习
在这里插入图片描述
先写总结后贴代码

1.通过代理模式实现设定通信协议下的方法异地调用

2.通过实现callable接口去保证执行的流程,call方法被代理对象调用,并且等待唤醒

遇到的一些问题

1.查自己电脑的ip地址,可以再cmd中输入“ipconfig”指令获取ip配置

2.中间NettyClientHandler的call()方法和channelRead()因为忘了synchronized加锁,运行客户端后报错无法走到最后一步,然后中间加了很多system.out进行检查,锁定问题再call方法上,发现没有加锁就调用了wait()方法,感觉应该是这个问题,百度了一下,发现确实是这个问题。在这里奉上解疑链接:为什么在Java中object.wait()/notify()方法必须在持有锁的情况下才能执行?

①先写一个总接口:HelloService`

package com.atguigu.bio.RPC;

/**
 * @author guiji
 * @create -2021-03-04-23:14
 */
//这个接口是服务和消费都需要的
public interface HelloService {
    String hello(String mes);

}

②再写一个实现类:HelloServiceImpl

import com.atguigu.bio.RPC.HelloService;

/**
 * @author guiji
 * @create -2021-03-04-23:49
 */
public class HelloServiceImpl implements HelloService {
    //当有消费者调用该方法时,返回一个结果
    public String hello(String mes) {
        System.out.println("收到客户端消息=" + mes);
        if(mes != null ){
            return "你好客户端,我是服务端,已经收到您的消息["+ mes + "]";
        }else{
            return "你好客户端,我是服务端,已经收到您的消息";
        }

    }
}

③写一个Server类

package com.atguigu.bio.dubbor.netty;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * @author guiji
 * @create -2021-03-04-23:55
 */
public class NettyServer {
    public static void startServer(String hostname,int port){
        System.out.println("准备启动服务端");
        startServer0(hostname,port);
    }
    //编写一个方法,完成对nettyserver的初始化和启动
    private static void startServer0(String hostname,int port){
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder());
                            pipeline.addLast(new StringEncoder());
                            pipeline.addLast(new NettyServerHandler());//添加自己的业务处理器
                        }
                    });
            ChannelFuture channelFuture;
            channelFuture = serverBootstrap.bind(hostname, port).sync();
            System.out.println("服务提供方开始提供服务");
            channelFuture.channel().closeFuture().sync();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

④写一个serverhandler

package com.atguigu.bio.dubbor.netty;

import com.atguigu.bio.dubbor.provider.HelloServiceImpl;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @author guiji
 * @create -2021-03-05-0:11
 */

public class NettyServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //获取客户端发过来的消息,并调用服务
        System.out.println("server的read方法被调用,msg= " + msg);
        //要求客户端消息符合某个协议才允许调用操作
        if(msg.toString().startsWith("hello")){
            System.out.println("消息符合协议,可以返回信息");
            String result = new HelloServiceImpl().hello(msg.toString().substring(msg.toString().lastIndexOf("#") + 1));
            ctx.writeAndFlush(result);
            System.out.println("返回信息:" + result);
        }else{
            System.out.println("消息有误,不予处理");
            ctx.writeAndFlush("result");
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

⑤写一个clienthandler类

package com.atguigu.bio.dubbor.netty;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.concurrent.Callable;

/**
 * @author guiji
 * @create -2021-03-05-0:39
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter implements Callable {
    private ChannelHandlerContext context;
    private String result;//服务器返回的结果
    private String para;//客户端调用方法时传入的参数

    //与服务器连接后就会被调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("NettyClientHandler的Active方法被调用" );
        context = ctx;
        System.out.println("NettyClientHandler的Active方法完毕" );
    }
    //收到服务器返回数据时会被调用
    @Override
    public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println(" channelRead 被调用  ");
        result = msg.toString();
        System.out.println("获取到服务端返回的result=" + result);
        notify();//唤醒等待线程
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

    public synchronized Object call() throws Exception {
        System.out.println(" call1 被调用  ");
        context.writeAndFlush(para);
        System.out.println("call方法发送para结束");
        wait();//等待channelRead获取到服务器结果后唤醒
        System.out.println(" call2 被调用  ");
        return result;
    }
    void setPara(String para){
        System.out.println("clientHandler的setPara方法被调用");
        this.para = para;
        System.out.println("para=" + para);
    }
}

⑥写一个client类

package com.atguigu.bio.dubbor.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import org.springframework.aop.TargetSource;

import java.lang.reflect.Proxy;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author guiji
 * @create -2021-03-05-1:09
 */
public class NettyClient {
    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    private static NettyClientHandler client;

    //编写方法使用代理模式获取一个代理对象

//    public Object getBean(final Class<?> serivceClass,final String providerName){
//        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class<?>[]{
//                serivceClass},(proxy,method,args) -> {
//            System.out.println("进入代理方法");
//            if(client == null){
//                System.out.println("初始化client");
//                initClient();
//            }
//            //设置要发给服务器的信息
//            //providerName是协议头,args[0]是客户端调用方法时的参数
//            System.out.println("获取到调用代理方法参数:" + args + ", 还有hello方法参数:" + args[0]);
//            client.setPara(providerName + args[0]);
//            return executor.submit(client).get();
//        });
//    }
    public Object getBean(final Class<?> serivceClass, final String providerName) {

        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class<?>[]{serivceClass}, (proxy, method, args) -> {

                    System.out.println("(proxy, method, args) 进入...." );
                    //{}  部分的代码,客户端每调用一次 hello, 就会进入到该代码
                    if (client == null) {
                        initClient();
                    }

                    //设置要发给服务器端的信息
                    //providerName 协议头 args[0] 就是客户端调用api hello(???), 参数
                    client.setPara(providerName + args[0]);

                    //
                    return executor.submit(client).get();

                });
    }


    //初始化客户端
    private static void initClient(){
        client = new NettyClientHandler();

        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY,true)
                .handler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(client);
                    }
                });
        try{
            bootstrap.connect("192.168.65.1",7070).sync();
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

⑦写一个serverbootstrap和clientbootstrap

package com.atguigu.bio.dubbor.customer;

import com.atguigu.bio.RPC.HelloService;
import com.atguigu.bio.dubbor.netty.NettyClient;
import com.atguigu.bio.dubbor.provider.HelloServiceImpl;

/**
 * @author guiji
 * @create -2021-03-05-1:41
 */
public class ClientBootstrap {

    //定义协议头
    public static final String providerName = "hello#";
    public static String res;

    public static void main(String[] args) {
        //创建一个消费者
        NettyClient customer = new NettyClient();

        //创建代理对象
        HelloService service = (HelloService) customer.getBean(HelloService.class,providerName);
        System.out.println("创建代理对象完毕:" );
//        System.out.println(service);//加上这行只能到达clientHandler的active方法,原因是之前忘了加锁
        //通过代理对象调用服务提供者的方法
        System.out.println("可以接受信息" + service.hello("您好我是客户端"));
        res = service.hello("你好");
        System.out.println("调用的结果res=" + res);

    }
}

public class ServerBootstrap {
    public static void main(String[] args) {
        NettyServer.startServer("192.168.65.1",7070);
    }
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页