v2.1
简单的小改一下 方便用户后面操作
-
将两个操作类集中一下顺便把注解完善,使得注解都在总的操作类上
-
启动方法选择注解的编写,选择是用netty还是nio实现rpc
package annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; //进行rpc工具的选择 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface RpcToolsSelector { String rpcTool() default "NIO"; }
-
总服务启动类
package service; import annotation.RpcMethodCluster; import annotation.RpcServerBootStrap; import annotation.RpcToolsSelector; import org.apache.zookeeper.KeeperException; import service.call.ChosenServerCall; import java.io.IOException; //总服务端启动类 用户调用 注解是 注册什么方法进去 //调用的是什么版本的服务端启动方法 @RpcMethodCluster(method = {"Hello","Bye"},startNum = {2,3}) @RpcServerBootStrap(version = "1.5") @RpcToolsSelector(rpcTool = "Nio") public class ServerCall { public static void main(String[] args) throws IOException, InterruptedException, KeeperException, NoSuchMethodException { ChosenServerCall.start(); } }
-
总客户端启动类
package service; import annotation.RpcClientBootStrap; import annotation.RpcToolsSelector; import exception.RpcException; import method.Customer; import service.call.ChosenClientCall; import java.io.IOException; //总客户端启动类 用户调用 什么版本的 和用什么工具 使用什么注册中心 序列化的选择 都可以用这个来玩 //注册中心不能给过去 这样就是重复依赖了 @RpcClientBootStrap(version = "1.5") @RpcToolsSelector(rpcTool = "Nio") public class ClientCall { public static void main(String[] args) throws RpcException, IOException, InterruptedException { //实现调用 Customer customer = ChosenClientCall.start(); System.out.println(customer.Hello("success")); System.out.println(customer.Bye("fail")); System.out.println(customer.Hello("fail")); } }
-
-
实现netty2.1版本,完成用户像调用自己方法一样进行调用外部方法,用了异步调用,代理模式
-
以netty为网络编程框架的消费者端启动类
package consumer.bootstrap.netty; import consumer.proxy.RpcNettyClientProxy; import method.Customer; /* 以netty为网络编程框架的消费者端启动类 */ //进行启动 提供类的方式即可 public class NettyConsumerBootStrap21 { public static Customer main(String[] args) throws InterruptedException { return (Customer) RpcNettyClientProxy.getBean(Customer.class); } }
-
代理类获取代理对象
package consumer.proxy; import annotation.RegistryChosen; import consumer.netty.NettyClient21; import consumer.service_discovery.NacosServiceDiscovery; import consumer.service_discovery.ZkCuratorDiscovery; import consumer.service_discovery.ZkServiceDiscovery; import exception.RpcException; import register.Register; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //就是获取代理类 通过代理类中的方法进行对应方法的执行和获取 public class RpcNettyClientProxy { private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); //参数 就是我要对其生成代理类的类 public static Object getBean(final Class<?> serviceClass) { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{serviceClass}, new InvocationHandler() { //根据对应的方法 代理类进行处理 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Object param = args[0]; //获取对应的方法地址 String methodAddress = getMethodAddress(methodName); String[] strings = methodAddress.split(":"); String hostName = strings[0]; int port = Integer.valueOf(strings[1]); //进行方法的调用 随即进行方法的调用 return NettyClient21.callMethod(hostName,port,param); } }); } /** * 实际去获得对应的服务 并完成方法调用的方法 * @param methodName 根据方法名 根据添加的注册中心注解来选择相应的注册中心进行 实现负载均衡获取一个方法对应地址 * @param * @return */ private static String getMethodAddress(String methodName) throws Exception { //根据注解进行方法调用 //根据在代理类上的注解调用 看清楚底下的因为是个class数组 可以直接继续获取 注解 RegistryChosen annotation = Register.class.getAnnotation(RegistryChosen.class); switch (annotation.registryName()) { case "nacos": return NacosServiceDiscovery.getMethodAddress(methodName); case "zookeeper": return ZkServiceDiscovery.getMethodAddress(methodName); case "zkCurator": return ZkCuratorDiscovery.getMethodAddress(methodName); default: throw new RpcException("不存在该注册中心"); } } }
-
代理类中实际实现远程调用方法 创建对应的客户端监听,进行方法的调用和写回
package consumer.netty; import consumer.netty_client_handler.NettyClientHandler21; import io.netty.bootstrap.Bootstrap; 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.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //实际客户端启动类 进行操作 //不确定能返回什么 所以判断是对象 public class NettyClient21 { //线程池 实现异步调用 private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); static NettyClientHandler21 clientHandler; public static void initClient(String hostName,int port) { clientHandler = new NettyClientHandler21(); //建立客户端监听 Bootstrap bootstrap = new Bootstrap(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { bootstrap.group(workGroup) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringEncoder());//传输必须加编解码器 不然不认识的类传不过去 pipeline.addLast(new StringDecoder()); pipeline.addLast(clientHandler); } }); //进行连接 bootstrap.connect(hostName, port).sync(); } catch (InterruptedException e) { e.printStackTrace(); } } public static Object callMethod(String hostName, int port, Object param) throws Exception { //我是有多个地方进行调用的 不能只连接一个 initClient(hostName,port); clientHandler.setParam(param); //接下来这就有关系到调用 直接调用 return executor.submit(clientHandler).get(); } }
-
实现异步调用的处理器
package consumer.netty_client_handler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.util.concurrent.Callable; //实现了Callable接口实现了异步调用 public class NettyClientHandler21 extends ChannelInboundHandlerAdapter implements Callable{ //传入的参数 private Object param; private Object response; private ChannelHandlerContext context; public void setParam(Object param) { this.param = param; } //当成功建立 就赋值上下文对象 @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { context = ctx; System.out.println("U•ェ•*U 成功连接"); } @Override public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { response = msg; notify(); } //调用的时候 就进行传输 @Override public synchronized Object call() throws Exception { context.writeAndFlush(param); wait(); return response; } //异常处理 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
以netty为网络编程框架的客户端启动类
package provider.bootstrap.netty; import provider.netty.NettyServer21; /* 以netty为网络编程框架的服务提供端启动类 */ //实现方法和之前都是比较一致的 每个对象要开一个线程去执行呢 原因是我们会启动同步等他们关闭 才出来 这样才能关闭对应的管道 public class NettyProviderBootStrap21 { static volatile int port = 6666; //对应的端口 要传过去 注册到注册中心去 public static void main(String[] args) throws InterruptedException { //直接在这里将对应的方法什么的进行分开 然后传过去 String methods = args[0]; String nums = args[1]; String[] methodArray = methods.split(","); String[] methodNumArray = nums.split(","); //进行创建 可能会出问题 这边的端口 for (int i = 0; i < methodArray.length; i++) { String methodName = methodArray[i]; for (Integer methodNum = 0; methodNum < Integer.valueOf(methodNumArray[i]); methodNum++) { new Thread(new Runnable() { @Override public void run() { try { NettyServer21.start(methodName,port++); } catch (InterruptedException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }).start(); } } } }
-
方法的绑定和注册
package provider.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; 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; import provider.netty_server_handler.NettyServerHandler21; import provider.utils.MethodRegister; //进行启动 绑定然后创建对应的 然后注册进注册中心去 public class NettyServer21 { public static void start(String methodName,int port) throws Exception { //真正的实现逻辑 被封装到下面的方法当中了 start0(methodName,port); } private static void start0(String methodName, int port) throws Exception { //先将地址进行注册 MethodRegister.register(methodName,"127.0.0.1",port); //开始创建相应的netty服务端 ServerBootstrap serverBootstrap = new ServerBootstrap(); //创建对应的工作组 用于处理不同的事件 EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workGroup = new NioEventLoopGroup(); try { //进行初始化 serverBootstrap.group(bossGroup,workGroup) .channel(NioServerSocketChannel.class) //自身实现的通道 .option(ChannelOption.SO_BACKLOG,128) //设置线程队列得到的连接个数 .childOption(ChannelOption.SO_KEEPALIVE,true) //设置保持活动连接状态 .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { System.out.println(socketChannel.remoteAddress()+"连接上了"); ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringEncoder()); pipeline.addLast(new StringDecoder()); pipeline.addLast(new NettyServerHandler21(methodName)); } }); ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); //对连接结果进行监听 channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) System.out.println("连接上"+port+"端口"); else System.out.println("连接端口失败"); } }); //等待关闭 同步 channelFuture.channel().closeFuture().sync(); } finally { //结束了的话 就进行关闭了 bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
-
服务端的处理器
package provider.netty_server_handler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import java.lang.reflect.Method; //实现简单的服务注册和回写 public class NettyServerHandler21 extends ChannelInboundHandlerAdapter { private String methodName; //要传入对应的方法名 否则不知道 netty服务器能执行什么方法 public NettyServerHandler21(String methodName) { this.methodName = methodName; } //实现对应的方法 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("收到来自"+ctx.channel().remoteAddress()+"的信息"); //使用反射的方法获取对应的类 通过反射再进行执行 Class<?> calledClass = Class.forName("provider.api."+methodName + "ServiceImpl"); Method method = calledClass.getMethod("say" + methodName, String.class); Object instance = calledClass.newInstance(); Object response = method.invoke(instance, msg.toString()); //获得对应信息并进行回传 ctx.writeAndFlush(response); } //出现异常的话 如何进行处理 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.channel().close(); cause.printStackTrace(); } }
-
-
bug:信息无法发送到服务端
原因: 传输的信息是String类型的所以必须要配备相应的编解码器 不然无法传输