使用spring框架进行整合
前文:构建Netty应用
项目地址: https://gitee.com/bigzibo/pp-netty-rpc
需求分析
- netty在启动spring的时候自动启动
- 需要编写代理类, 代理需要通信的接口
- 需要使用rpc的接口在spring生成bean之前被代理
- 通过服务或接口名与方法名找到服务端中对应的方法
- 接口名对应的实现类在spring容器中做预处理
获取spring的上下文
通过实现ApplicationContextAware接口, 重写setApplicationContext就可以获得上下文,
spring会自动找到实现这个接口的类, 调用重写的这个方法
client
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.bootstrap = new Bootstrap();
// 这里的group中的eventLoop只负责监听channel状态
// 如果channel空闲则可以发送数据
// 如果channel中有回传则接收数据
this.group = new NioEventLoopGroup(1);
this.bootstrap.group(group).
channel(NioSocketChannel.class).
option(ChannelOption.TCP_NODELAY, true).
option(ChannelOption.SO_KEEPALIVE, true).
handler(this);
String zkAddress = applicationContext.getEnvironment().getProperty("zookeeper.address");
// 生成zookeeper客户端
ZookeeperClient zkClient = new ZookeeperClient(zkAddress);
// manager是负责管理缓存和生成channel连接的类
this.manager = new NettyChannelManager(this, zkClient);
this.handler = new NettyClientHandler();
}
客户端中还实现了InitializingBean接口, afterPropertiesSet方法会在初始化所有属性之后调用,
@Override
public void afterPropertiesSet(){
// TODO do something...
manager.refresh();
}
这里我们调用了refresh方法, 即程序第一次刷新manager缓存的地方, 缓存中存放着服务与服务端地址, 地址与channel的关系
我们自定义了一个Sender接口, 该类实现了这个接口
send方法是客户端与服务端通信的起点, 获取channel并且获取返回结果
@Override
public Object send(Request request) throws InterruptedException {
Channel channel = null;
Class<?> clazz = null;
try {
clazz = Class.forName(request.getClassName());
// 分布式下, 一个服务会部署到多台服务器, 所以这里用take轮询获得channel
channel = manager.take(clazz.getSimpleName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if (channel != null && channel.isActive()) {
SynchronousQueue<Object> queue = this.handler.sendRequest(request, channel);
// 等待结果返回, queue为空的时候, 这里会阻塞
Object result = queue.take();
return JSONArray.toJSONString(result);
} else {
Response res = new Response();
res.setCode(502);
assert clazz != null;
res.setError("未正确连接到服务器.请检查相关配置信息! 服务名:" + clazz.getSimpleName());
return JSONArray.toJSONString(res);
}
}
server
在服务端中, 获取上下文需要处理的事情除了client中初始化netty之外, 还需要通过找到注解并且反射得到服务名与服务实现类的数据结构
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
String address = applicationContext.getEnvironment().getProperty("remoteAddress");
String zkAddress = applicationContext.getEnvironment().getProperty("zookeeper.address");
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(NettyService.class);
for (Object serviceBean : beans.values()) {
Class<?> clazz = serviceBean.getClass();
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> inter : interfaces) {
// 找到@NettyService注解下的类, 获得其实现的接口
// 将接口全类名作为key, 存入map中
String interfaceSimpleName = inter.getSimpleName();
String interfaceName = inter.getName();
// 这里注册入zookeeper
ServiceRegistry serviceRegistry = new ServiceRegistry(zkAddress);
// 使用简单类名在zookeeper中注册
serviceRegistry.register(interfaceSimpleName, address);
serviceMap.put(interfaceName, serviceBean);
}
}
logger.info("已加载全部服务接口");
// 新建一个serverWorker, 即前文中的netty初始化
runner = new ServerWorker(address, serviceMap);
}
@Override
public void afterPropertiesSet() throws Exception {
runner.open();
}
在前文的channelRead方法中, 我们只提起了一个名为handler的方法, 但是并未做代码展示, 因为那与netty无关, 所以放在这边作展示
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Request request = JSON.parseObject(msg.toString(), Request.class);
if ("heartBeat".equals(request.getMethodName())) {
log.info("客户端心跳信息..."+ctx.channel().remoteAddress());
return;
}
Response response = new Response();
response.setRequestId(request.getId());
try {
Object res = this.handler(request);
response.setData(res);
} catch (Exception e) {
response.setCode(-1);
response.setError(e.getMessage());
log.error("请求调用失败", e);
}
ctx.writeAndFlush(response);
}
private Object handler(Request request) throws Exception {
String className = request.getClassName();
// 根据className去拿反射的实现类
// 前文中, serviceMap存的是全类名
Object serviceBean = serviceMap.get(className);
String methodName = request.getMethodName();
Object[] parameters = request.getParameters();
// 得到需要被反射的类, 方法, 参数后调用代理方法
return MethodUtils.invokeMethod(serviceBean, methodName, parameters);
}
Registrar
registrar包下的类, 功能就是用于注册beanDefinition, spring是依据beanDefinition来生成bean的
这边主要讲一下client的注册器, 因为server的与client的功能类似, 并且client多一个设置工厂来代理的过程.
public class NettyClientScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* @Description: 在spring生成bean之前
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获得的注册器传入扫描类, 并且新建扫描类
NettyClientInterfaceScanner scanner = new NettyClientInterfaceScanner(registry);
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
AnnotationAttributes annoAttrs =
AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(NettyClientScan.class.getName()));
// 这里得到包下的类工厂
Class<? extends NettyFactoryBean> nettyFactoryBeanClass = annoAttrs.getClass("factoryBean");
// 如果不是使用我们的自定义类工厂则设置使用
if (!NettyFactoryBean.class.equals(nettyFactoryBeanClass)) {
scanner.setNettyFactoryBean(BeanUtils.instantiateClass(nettyFactoryBeanClass));
}
List<String> basePackages = new ArrayList<>();
// 通过basePackages的路径扫描包
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
// 将路径下的包加入扫描
scanner.setAnnotationClass(NettyClient.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
}
Scanner类, 继承了spring 的扫描类
public class NettyClientInterfaceScanner extends ClassPathBeanDefinitionScanner {
private NettyFactoryBean nettyFactoryBean = new NettyFactoryBean();
private Class<? extends Annotation> annotationClass;
public NettyClientInterfaceScanner(BeanDefinitionRegistry registry) {
super(registry);
}
// 这里设置扫描哪个注解
public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
this.annotationClass = annotationClass;
}
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 由于注册了过滤器, 所以这里扫描出来的bean都是带有注解的
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
logger.warn("No netty interfaces was found in '"
+ Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
// client需要被代理, 所以比server的多一步
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
// 注册过滤器, 过滤不含有annotationClass注解的类
public void registerFilters() {
boolean acceptAllInterfaces = true;
if (this.annotationClass != null) {
addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
acceptAllInterfaces = false;
}
if (acceptAllInterfaces) {
addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
}
// exclude package-info.java
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
String className = metadataReader.getClassMetadata()
.getClassName();
return className.endsWith("package-info");
});
}
/**
* @Description: 将生成该类的bean工厂设置为NettyBeanFactory
*/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
// bean信息中设置bean工厂为我们自定义的bean工厂
definition.setBeanClass(this.nettyFactoryBean.getClass());
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
@Override
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
return metadataReader.getClassMetadata().isInterface() && metadataReader.getClassMetadata().isIndependent();
}
public void setNettyFactoryBean(NettyFactoryBean instantiateClass) {
this.nettyFactoryBean = instantiateClass != null ? instantiateClass : this.nettyFactoryBean;
}
}
经过扫描和注册工厂之后, 经过扫描的接口都会被自定义的工厂生成
自定义的Bean工厂
public class NettyFactoryBean<T> implements FactoryBean<T> {
private Class<T> nettyInterface;
public NettyFactoryBean() {}
public NettyFactoryBean(Class<T> nettyInterface) {
this.nettyInterface = nettyInterface;
}
// 打了@NettyClient注解的类被Autowired的时候, 使用这里的getObj
@Override
public T getObject() throws Exception {
// 返回代理, 代理类是我们的invoker
return (T) Proxy.newProxyInstance(nettyInterface.getClassLoader(), new Class[]{nettyInterface}, NettyInterfaceInvoker.getInstance());
}
@Override
public Class<?> getObjectType() {
return this.nettyInterface;
}
@Override
public boolean isSingleton() {
return true;
}
}
在spring依赖注入的过程中, 我们将工厂设置为了自定义的工厂, 所以代理也使用了我们的代理类NettyInterfaceInvoker
代理类
NettyInterfaceInvoker
重点放在invoke方法上
/**
* @Description: 在FactoryBean中的getObject方法中的invoker类是该类, 所以该工厂生成的bean都会被该invoke方法代理
* 这里完成了我们的基本模型, client调用service, 通过代理走netty通信调用服务端的实现类
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Request request = new Request();
// 初始化request设置参数
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameters(args);
request.setParameterTypes(method.getParameterTypes());
// 线程特有的id, 多个线程访问一个channel的时候需要使用id判断
request.setId(UUID.randomUUID().toString());
// 通过netty去调用真正的服务, 获得回传
Object result = sender.send(request);
Class<?> returnType = method.getReturnType();
// 如果返回的结果是502, 则说明服务器出现了一些问题
Response response = JSON.parseObject(result.toString(), Response.class);
if (response.getCode() == 502) {
throw new Exception(response.getError());
}
// 根据method的返回值处理response
if (returnType.isPrimitive() || String.class.isAssignableFrom(returnType)) {
return response.getData();
} else if (Collection.class.isAssignableFrom(returnType)) {
return JSONArray.parseArray(response.getData().toString(), Object.class);
} else if (Map.class.isAssignableFrom(returnType)) {
return JSON.parseObject(response.getData().toString(), Map.class);
} else {
Object data = response.getData();
return JSONObject.parseObject(data.toString(), returnType);
}
}
后记
以上则是有关spring的内容, 其实spring还是比较复杂的, 所以有些扫描注册的内容也都是网络上的模板套一套.
不过核心的内容就是实现一个自己的bean工厂, 在依赖注入service的时候, 代理类使用到的是自己的invoke
下文会讲一下manager和zookeeper, 因为我们需要将服务注册到zookeeper上, 所以获取服务和刷新服务缓存的内容需要整合到zookeeper去说明