天气:阴雨
用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);
}
}