一。rpc远程过程调用,我的一个操作,是远程方法调用给的结果,这样做增加服务的复用性。
写rpc,要把消息传递给远程,消息包括
既然是方法调用,一个方法的唯一标志是类名,方法名,参数类型,方法参数。netty异步准确的返回结果,给谁的结果呢,这就要传递一个唯一标识ID。
RPC远程调用跟本地调用相似,并对方法进行扩充,这就要用到设计模式,装饰者,代理模式
这里用代理模式。如果想不断扩充功能可用装饰者模式。
服务注册,这里用zookeeper实现。
服务端代码实现:加入依赖
一,主服务类实现spring容器管理,实现俩接口ApplicationContextAware, InitializingBean
实现了ApplicationContextAware之后,这个类就可以方便获得ApplicationContext中的所有bean。换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。
InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候都会执行该方法。完成netty服务启动,服务注册。
代码:
public class RpcServer implements ApplicationContextAware, InitializingBean {
private static final Logger LOGGER = LoggerFactory.getLogger(RpcServer.class);
//server.address=127.0.0.1:8888 netty服务地址
private String serverAddress;
//registry.address=127.0.0.1:2181 注册中心地址
private ServiceRegistry serviceRegistry;
/**
* 存放接口名与服务对象之间的映射关系
*/
private Map
public RpcServer(String serverAddress) {
this.serverAddress = serverAddress;
}
public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) {
this.serverAddress = serverAddress;
this.serviceRegistry = serviceRegistry;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
// 获取所有带有RpcService注解的SpringBean
Map
if (MapUtils.isNotEmpty(serviceBeanMap)) {
for (Object serviceBean : serviceBeanMap.values()) {
String interfaceName = serviceBean.getClass().getAnnotation(RpcService.class).value().getName();
handlerMap.put(interfaceName, serviceBean);
}
}
}
@Override
public void afterPropertiesSet() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer
@Override
public void initChannel(SocketChannel channel) {
channel.pipeline().addLast(
/** 将RPC请求进行解码(为了处理请求)*/
new RpcDecoder(RpcRequest.class))
/** 将RPC响应进行编码(为了返回响应)*/
.addLast(new RpcDecoder(RpcResponse.class))
/** 处理RPC请求*/
.addLast(new RpcHandler(handlerMap));
}
}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
String[] array = serverAddress.split(":");
String host = array[0];
int port = Integer.parseInt(array[1]);
ChannelFuture future = bootstrap.bind(host, port).sync();
LOGGER.debug("server started on port {}", port);
if (serviceRegistry != null) {
serviceRegistry.register(serverAddress); // 注册服务地址
}
future.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
//netty服务处理类
public class RpcHandler extends SimpleChannelInboundHandler
private static final Logger LOGGER = LoggerFactory.getLogger(RpcHandler.class);
private final Map
public RpcHandler(Map
this.handlerMap = handlerMap;
}
@Override
public void channelRead0(final ChannelHandlerContext ctx, RpcRequest request) throws Exception {
RpcResponse response = new RpcResponse();
response.setRequestId(request.getRequestId());
try {
Object result = handle(request);
response.setResult(result);
} catch (Throwable t) {
response.setError(t);
}
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
private Object handle(RpcRequest request) throws Throwable {
String className = request.getClassName();
Object serviceBean = handlerMap.get(className);
Class> serviceClass = serviceBean.getClass();
String methodName = request.getMethodName();
Class>[] parameterTypes = request.getParameterTypes();
Object[] parameters = request.getParameters();
FastClass serviceFastClass = FastClass.create(serviceClass);
FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
return serviceFastMethod.invoke(serviceBean, parameters);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.error("server caught exception", cause);
ctx.close();
}
}
//实现服务注册
public class ServiceRegistry {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistry.class);
/**
*
*/
private CountDownLatch latch = new CountDownLatch(1);
private String registryAddress;
public ServiceRegistry(String registryAddress) {
this.registryAddress = registryAddress;
}
public void register(String data) {
if (data != null) {
ZooKeeper zk = connectServer();
if (zk != null) {
createNode(zk, data);
}
}
}
private ZooKeeper connectServer() {
ZooKeeper zk = null;
try {
zk = new ZooKeeper(registryAddress, Constant.ZK_SESSION_TIMEOUT, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
latch.countDown();
}
});
latch.await();
} catch (IOException | InterruptedException e) {
LOGGER.error("Can't get connection for zookeeper,registryAddress:{}", registryAddress, e);
}
return zk;
}
private void createNode(ZooKeeper zk, String data) {
try {
byte[] bytes = data.getBytes();
String znodePath = Constant.ZK_DATA_PATH;
String[] paths = znodePath.split("/");
String path = "";
for (int i = 1; i < paths.length; i++) {
path = path +"/"+ paths[i];
Stat exists = zk.exists(path, null);
if(exists == null){
if(i == paths.length-1){
zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
zk.create(path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
LOGGER.info("create zookeeper node ({} => {})", path, data);
}else{
zk.create(path, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
LOGGER.info("create zookeeper node ({} => {})", path, data);
}
}
}
//String path = zk.create(Constant.ZK_DATA_PATH, bytes, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
LOGGER.debug("create zookeeper node ({} => {})", path, data);
} catch (Exception e) {
LOGGER.error("", e);
}
}
}
//服务入口类
public class RpcBootstrap {
@SuppressWarnings("resource")
public static void main(String[] args) {
new ClassPathXmlApplicationContext("spring-server.xml");
}
}
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Component
// 表明可被 Spring 扫描
public @interface RpcService {
Class> value();
}
// 指定远程接口 测试类
@RpcService(HelloService.class)
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
return "Hello! " + name;
}
}
public interface HelloService {
String hello(String name);
}
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
registry.address=127.0.0.1:2181
server.address=127.0.0.1:8000