[年薪60W分水岭]基于Netty手写Apache Dubbo(带注册中心和注解)
阅读这篇文章之前,建议先阅读和这篇文章关联的内容。
1. 详细剖析分布式微服务架构下网络通信的底层实现原理(图解)
2. (年薪60W的技巧)工作了5年,你真的理解Netty以及为什么要用吗?(深度干货)
4. BAT面试必问细节:关于Netty中的ByteBuf详解
5. 通过大量实战案例分解Netty中是如何解决拆包黏包问题的?
6. 基于Netty实现自定义消息通信协议(协议设计及解析应用实战)
8. 手把手教你基于Netty实现一个基础的RPC框架(通俗易懂)
在本篇文章中,我们继续围绕Netty手写实现RPC基础篇进行优化,主要引入几个点
- 集成spring,实现注解驱动配置
- 集成zookeeper,实现服务注册
- 增加负载均衡实现
源代码,加「跟着Mic学架构」微信号,回复『rpc』获取。
增加注解驱动
主要涉及到的修改模块
- netty-rpc-protocol
- netty-rpc-provider
netty-rpc-protocol
当前模块主要修改的类如下。

图7-1
下面针对netty-rpc-protocol模块的修改如下
增加注解驱动
这个注解的作用是用来指定某些服务为远程服务
@Target(ElementType.TYPE)// Target说明了Annotation所修饰的对象范围, TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention(RetentionPolicy.RUNTIME)// Reteniton的作用是定义被它所注解的注解保留多久,保留至运行时。所以我们可以通过反射去获取注解信息。
@Component
public @interface GpRemoteService {
}
SpringRpcProviderBean
这个类主要用来在启动NettyServer,以及保存bean的映射关系
@Slf4j
public class SpringRpcProviderBean implements InitializingBean, BeanPostProcessor {
private final int serverPort;
private final String serverAddress;
public SpringRpcProviderBean(int serverPort) throws UnknownHostException {
this.serverPort = serverPort;
InetAddress address=InetAddress.getLocalHost();
this.serverAddress=address.getHostAddress();
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("begin deploy Netty Server to host {},on port {}",this.serverAddress,this.serverPort);
new Thread(()->{
try {
new NettyServer(this.serverAddress,this.serverPort).startNettyServer();
} catch (Exception e) {
log.error("start Netty Server Occur Exception,",e);
e.printStackTrace();
}
}).start();
}
//bean实例化后调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean.getClass().isAnnotationPresent(GpRemoteService.class)){ //针对存在该注解的服务进行发布
Method[] methods=bean.getClass().getDeclaredMethods();
for(Method method: methods){ //保存需要发布的bean的映射
String key=bean.getClass().getInterfaces()[0].getName()+"."+method.getName();
BeanMethod beanMethod=new BeanMethod();
beanMethod.setBean(bean);
beanMethod.setMethod(method);
Mediator.beanMethodMap.put(key,beanMethod);
}
}
return bean;
}
}
Mediator
主要管理bean以及调用
BeanMethod
@Data
public class BeanMethod {
private Object bean;
private Method method;
}
Mediator
负责持有发布bean的管理,以及bean的反射调用
public class Mediator {
public static Map<String,BeanMethod> beanMethodMap=new ConcurrentHashMap<>();
private volatile static Mediator instance=null;
private Mediator(){
}
public static Mediator getInstance(){
if(instance==null){
synchronized (Mediator.class){
if(instance==null){
instance=new Mediator();
}
}
}
return instance;
}
public Object processor(RpcRequest rpcRequest){
String key=rpcRequest.getClassName()+"."+rpcRequest.getMethodName();
BeanMethod beanMethod=beanMethodMap.get(key);
if(beanMethod==null){
return null;
}
Object bean=beanMethod.getBean();
Method method=beanMethod.getMethod();
try {
return method.invoke(bean,rpcRequest.getParams());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
RpcServerProperties
定义配置属性
@Data
@ConfigurationProperties(prefix = "gp.rpc")
public class RpcServerProperties {
private int servicePort;
}
RpcProviderAutoConfiguration
定义自动配置类
@Configuration
@EnableConfigurationProperties(RpcServerProperties.class)
public class RpcProviderAutoConfiguration {
@Bean
public SpringRpcProviderBean rpcProviderBean(RpcServerProperties rpcServerProperties) throws UnknownHostException {
return new SpringRpcProviderBean(rpcServerProperties.getServicePort());
}
}
修改RpcServerHandler
修改调用方式,直接使用Mediator的调用即可。
public class RpcServerHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcRequest>> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, RpcProtocol<RpcRequest> msg) throws Exception {
RpcProtocol resProtocol=new RpcProtocol<>();
Header header=msg.getHeader();
header.setReqType(ReqType.RESPONSE.code());
Object result=Mediator.getInstance().processor(msg.getContent()); //主要修改这个部分
resProtocol.setHeader(header);
RpcResponse response=new RpcResponse();
response.setData(result);
response.setMsg("success");
resProtocol.setContent(response);
ctx.writeAndFlush(resProtocol);
}
}
netty-rpc-provider
这个模块中主要修改两个部分
- application.properties
- NettyRpcProviderMain
NettyRpcProviderMain
@ComponentScan(basePackages = {"com.example.spring.annotation","com.example.spring.service","com.example.service"})
@SpringBootApplication
public class NettyRpcProviderMain {
public static void main(String[] args) throws Exception {
SpringApplication.run(NettyRpcProviderMain.class, args);
//去掉原来的实例化部分
}
}
application.properties
增加一个配置属性。
gp.rpc.servicePort=20880
UserServiceImpl
把当前服务发布出去。
@GpRemoteService //表示将当前服务发布成远程服务
@Slf4j
public class UserServiceImpl implements IUserService {
@Override
public String saveUser(String name) {
log.info("begin saveUser:"+name);
return "Save User Success!";
}
}
修改客户端的注解驱动
客户端同样也需要通过注解的方式来引用服务,这样就能够彻底的屏蔽掉远程通信的细节内容,代码结构如图7-2所示

图7-2
增加客户端注解
在netty-rpc-protocol模块的annotation目录下创建下面这个注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Autowired
public @interface GpRemoteReference {
}
SpringRpcReferenceBean
定义工厂Bean,用来构建远程通信的代理
public class SpringRpcReferenceBean implements FactoryBean<Object> {
private Class<?> interfaceClass;
private Object object;
private String serviceAddress;
private int servicePort;
@Override
public Object getObject() throws Exception {
return object;
}
public void init(){
this.object= Proxy.newProxyInstance(this.interfaceClass.getClassLoader(),
new Class<?>[]{this.interfaceClass},
new RpcInvokerProxy(this.serviceAddress,this.servicePort));
}
@Override
public Class<?> getObjectType() {
return this.interfaceClass;
}
public void setInterfaceClass(Class<?> interfaceClass) {
this.interfaceClass = interfaceClass;
}
public void setServiceAddress(String serviceAddress) {
this.serviceAddress = serviceAddress;
}
public void setServicePort(int servicePort) {
this.servicePort = servicePort;
}
}
SpringRpcReferencePostProcessor
用来实现远程Bean的动态代理注入:
BeanClassLoaderAware: 获取Bean的类装载器
BeanFactoryPostProcessor:在spring容器加载了bean的定义文件之后,在bean实例化之前执行
ApplicationContextAware: 获取上下文对象ApplicationContenxt
@Slf4j
public class SpringRpcReferencePostProcessor implements ApplicationContextAware, BeanClassLoaderAware, BeanFactoryPostProcessor {
private ApplicationContext context;
private ClassLoader classLoader;
private RpcClientProperties clientProperties;
public SpringRpcReferencePostProcessor(RpcClientProperties clientProperties) {
this.clientProperties = clientProperties;
}
//保存发布的引用bean信息
private final Map<String, BeanDefinition> rpcRefBeanDefinitions=new ConcurrentHashMap<>();
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader=classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context=applicationContext;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefinitionname:beanFactory.getBeanDefinitionNames()){
//遍历bean定义,然后获取到加载的bean,遍历这些bean中的字段,是否携带GpRemoteReference注解
//如果有,则需要构建一个动态代理实现
BeanDefinition beanDefinition=beanFactory.getBeanDefinition(beanDefinitionname);
String beanClassName=beanDefinition.getBeanClassName();
if(beanClassName!=null){
//和forName方法相同,内部就是直接调用的forName方法
Class<?> clazz=ClassUtils.resolveClassName(beanClassName,this.classLoader);
//针对当前类中的指定字段,动态创建一个Bean
ReflectionUtils.doWithFields(clazz,this::parseRpcReference);
}
}
//将@GpRemoteReference注解的bean,构建一个动态代理对象
BeanDefinitionRegistry registry=(BeanDefinitionRegistry)beanFactory;
this.rpcRefBeanDefinitions.forEach((beanName,beanDefinition)->{
if(context.containsBean(beanName)){
log.warn("SpringContext already register bean {}",beanName);
return;
}
//把动态创建的bean注册到容器中
registry.registerBeanDefinition(beanName,beanDefinition);
log.info("registered RpcReferenceBean {} success.",beanName);
});
}
private void parseRpcReference(Field field){
GpRemoteReference gpRemoteReference=AnnotationUtils.getAnnotation(field,GpRemoteReference.class);
if(gpRemoteReference!=null) {
BeanDefinitionBuilder builder=BeanDefinitionBuilder.genericBeanDefinition(SpringRpcReferenceBean.class);
builder.setInitMethodName(RpcConstant.INIT_METHOD_NAME);
builder.addPropertyValue("interfaceClass",field.getType());
builder.addPropertyValue("serviceAddress",clientProperties.getServiceAddress());
builder.addPropertyValue("servicePort",clientProperties.getServicePort());
BeanDefinition beanDefinition=builder.getBeanDefinition();
rpcRefBeanDefinitions.put(field.getName(),beanDefinition);
}
}
}
需要在RpcConstant常量中增加一个INIT_METHOD_NAME属性
public class RpcConstant {
//header部分的总字节数
public final static int HEAD_TOTAL_LEN=16;
//魔数
public final static short MAGIC=0xca;
public static final String INIT_METHOD_NAME = "init";
}
RpcClientProperties
@Data
public class RpcClientProperties {
private String serviceAddress="192.168.1.102";
private int servicePort=20880;
}
RpcRefernceAutoConfiguration
@Configuration
public class RpcRefernceAutoConfiguration implements EnvironmentAware{
@Bean
public SpringRpcReferencePostProcessor postProcessor(){
String address=environment.getProperty("gp.serviceAddress");
int port=Integer.parseInt(environment.getProperty("gp.servicePort"));
RpcClientProperties rc=new RpcClientProperties();
rc.setServiceAddress(address);
rc.setServicePort(port);
return new SpringRpcReferencePostProcessor(rc);
}
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment=environment;
}
}
netty-rpc-consumer
修改netty-rpc-consumer模块
- 把该模块变成一个spring boot项目
- 增加web依赖
- 添加测试类

图7-3 netty-rpc-consumer模块
引入jar包依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
HelloController
@RestController
public class HelloController {
@GpRemoteReference
private IUserService userService;
@GetMapping("/test")
public String test(){
return userService.saveUser("Mic");
}
}
NettyConsumerMain
@ComponentScan(basePackages = {"com.example.spring.annotation","com.example.controller","com.example.spring.reference"})
@SpringBootApplication
public class NettyConsumerMain {
public static void main(String[] args) {
SpringApplication.run(NettyConsumerMain.class, args);
}
}
application.properties
gp.serviceAddress=192.168.1.102
servicePort.servicePort=20880
访问测试
- 启动Netty-Rpc-Server
- 启动Netty-Rpc-Consumer
如果启动过程没有任何问题,则可以访问HelloController来测试远程服务的访问。
引入注册中心
创建一个netty-rpc-registry模块,代码结构如图7-4所示。

引入相关依赖
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
<version>4.2.0</version>
</dependency>
IRegistryService
public interface IRegistryService {
/**
* 注册服务
* @param serviceInfo
* @throws Exception
*/
void register(ServiceInfo serviceInfo) throws Exception;
/**
* 取消注册
* @param serviceInfo
* @throws Exception
*/
void unRegister(ServiceInfo serviceInfo) throws Exception;
/**
* 动态发现服务
* @param serviceName
* @return
* @throws Exception
*/
ServiceInfo discovery(String serviceName) throws Exception;
}
ServiceInfo
@Data
public class ServiceInfo {
private String serviceName;
private String serviceAddress;
private int servicePort;
}
ZookeeperRegistryService
@Slf4j
public class ZookeeperRegistryService implements IRegistryService {
private static final String REGISTRY_PATH="/registry";
//Curator中提供的服务注册与发现的组件封装,它对此抽象出了ServiceInstance、
// ServiceProvider、ServiceDiscovery三个接口,通过它我们可以很轻易的实现Service Discovery
private final ServiceDiscovery<ServiceInfo> serviceDiscovery;
private ILoadBalance<ServiceInstance<ServiceInfo>> loadBalance;
public ZookeeperRegistryService(String registryAddress) throws Exception {
CuratorFramework client= CuratorFrameworkFactory
.newClient(registryAddress,new ExponentialBackoffRetry(1000,3));
JsonInstanceSerializer<ServiceInfo> serializer=new JsonInstanceSerializer<>(ServiceInfo.class);
this.serviceDiscovery= ServiceDiscoveryBuilder.builder(ServiceInfo.class)
.client(client)
.serializer(serializer)
.basePath(REGISTRY_PATH)
.build();
this.serviceDiscovery.start();
loadBalance=new RandomLoadBalance();
}
@Override
public void register(ServiceInfo serviceInfo) throws Exception {
log.info("开始注册服务,{}",serviceInfo);
ServiceInstance<ServiceInfo> serviceInstance=ServiceInstance
.<ServiceInfo>builder().name(serviceInfo.getServiceName())
.address(serviceInfo.getServiceAddress())
.port(serviceInfo.getServicePort())
.payload(serviceInfo)
.build();
serviceDiscovery.registerService(serviceInstance);
}
@Override
public void unRegister(ServiceInfo serviceInfo) throws Exception {
ServiceInstance<ServiceInfo> serviceInstance=ServiceInstance.<ServiceInfo>builder()
.name(serviceInfo.getServiceName())
.address(serviceInfo.getServiceAddress())
.port(serviceInfo.getServicePort())
.payload(serviceInfo)
.build();
serviceDiscovery.unregisterService(serviceInstance);
}
@Override
public ServiceInfo discovery(String serviceName) throws Exception {
Collection<ServiceInstance<ServiceInfo>> serviceInstances= serviceDiscovery
.queryForInstances(serviceName);
//通过负载均衡返回某个具体实例
ServiceInstance<ServiceInfo> serviceInstance=loadBalance.select((List<ServiceInstance<ServiceInfo>>)serviceInstances);
if(serviceInstance!=null){
return serviceInstance.getPayload();
}
return null;
}
}
引入负载均衡算法
由于服务端发现服务时可能有多个,所以需要用到负载均衡算法来实现
ILoadBalance
public interface ILoadBalance<T> {
T select(List<T> servers);
}
AbstractLoadBalance
public abstract class AbstractLoadBanalce implements ILoadBalance<ServiceInstance<ServiceInfo>> {
@Override
public ServiceInstance<ServiceInfo> select(List<ServiceInstance<ServiceInfo>> servers){
if(servers==null||servers.size()==0){
return null;
}
if(servers.size()==1){
return servers.get(0);
}
return doSelect(servers);
}
protected abstract ServiceInstance<ServiceInfo> doSelect(List<ServiceInstance<ServiceInfo>> servers);
}
RandomLoadBalance
public class RandomLoadBalance extends AbstractLoadBanalce {
@Override
protected ServiceInstance<ServiceInfo> doSelect(List<ServiceInstance<ServiceInfo>> servers) {
int length=servers.size();
Random random=new Random();
return servers.get(random.nextInt(length));
}
}
RegistryType
public enum RegistryType {
ZOOKEEPER((byte)0),
EUREKA((byte)1);
private byte code;
RegistryType(byte code) {
this.code=code;
}
public byte code(){
return this.code;
}
public static RegistryType findByCode(byte code) {
for (RegistryType rt : RegistryType.values()) {
if (rt.code() == code) {
return rt;
}
}
return null;
}
}
RegistryFactory
public class RegistryFactory {
public static IRegistryService createRegistryService(String address,RegistryType registryType){
IRegistryService registryService=null;
try {
switch (registryType) {
case ZOOKEEPER:
registryService = new ZookeeperRegistryService(address);
break;
case EUREKA:
//TODO
break;
default:
registryService = new ZookeeperRegistryService(address);
break;
}
}catch (Exception e){
e.printStackTrace();
}
return registryService;
}
}
修改服务端增加服务注册
修改netty-rpc-protocol模块,加入注册中心的支持
SpringRpcProviderBean
按照下面case标注部分,表示要修改的内容
@Slf4j
public class SpringRpcProviderBean implements InitializingBean, BeanPostProcessor {
private final int serverPort;
private final String serverAddress;
private final IRegistryService registryService; //修改部分,增加注册中心实现
public SpringRpcProviderBean(int serverPort,IRegistryService registryService) throws UnknownHostException {
this.serverPort = serverPort;
InetAddress address=InetAddress.getLocalHost();
this.serverAddress=address.getHostAddress();
this.registryService=registryService; //修改部分,增加注册中心实现
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("begin deploy Netty Server to host {},on port {}",this.serverAddress,this.serverPort);
new Thread(()->{
try {
new NettyServer(this.serverAddress,this.serverPort).startNettyServer();
} catch (Exception e) {
log.error("start Netty Server Occur Exception,",e);
e.printStackTrace();
}
}).start();
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean.getClass().isAnnotationPresent(GpRemoteService.class)){ //针对存在该注解的服务进行发布
Method[] methods=bean.getClass().getDeclaredMethods();
for(Method method: methods){
String serviceName=bean.getClass().getInterfaces()[0].getName();
String key=serviceName+"."+method.getName();
BeanMethod beanMethod=new BeanMethod();
beanMethod.setBean(bean);
beanMethod.setMethod(method);
Mediator.beanMethodMap.put(key,beanMethod);
try {
//修改部分,增加注册中心实现
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.setServiceAddress(this.serverAddress);
serviceInfo.setServicePort(this.serverPort);
serviceInfo.setServiceName(serviceName);
registryService.register(serviceInfo);//修改部分,增加注册中心实现
}catch (Exception e){
log.error("register service {} faild",serviceName,e);
}
}
}
return bean;
}
}
RpcServerProperties
修改RpcServerProperties,增加注册中心的配置
@Data
@ConfigurationProperties(prefix = "gp.rpc")
public class RpcServerProperties {
private int servicePort;
private byte registerType;
private String registryAddress;
}
RpcProviderAutoConfiguration
增加注册中心的注入。
@Configuration
@EnableConfigurationProperties(RpcServerProperties.class)
public class RpcProviderAutoConfiguration {
@Bean
public SpringRpcProviderBean rpcProviderBean(RpcServerProperties rpcServerProperties) throws UnknownHostException {
//添加注册中心
IRegistryService registryService=RegistryFactory.createRegistryService(rpcServerProperties.getRegistryAddress(), RegistryType.findByCode(rpcServerProperties.getRegisterType()));
return new SpringRpcProviderBean(rpcServerProperties.getServicePort(),registryService);
}
}
application.properties
修改netty-rpc-provider中的application.properties。
gp.rpc.servicePort=20880
gp.rpc.registerType=0
gp.rpc.registryAddress=192.168.221.128:2181
修改客户端,增加服务发现
客户端需要修改的地方较多,下面这些修改的代码,都是netty-rpc-protocol模块中的类。
RpcClientProperties
增加注册中心类型和注册中心地址的选项
@Data
public class RpcClientProperties {
private String serviceAddress="192.168.1.102";
private int servicePort=20880;
private byte registryType;
private String registryAddress;
}
修改NettyClient
原本是静态地址,现在修改成了从注册中心获取地址
@Slf4j
public class NettyClient {
private final Bootstrap bootstrap;
private final EventLoopGroup eventLoopGroup=new NioEventLoopGroup();
/* private String serviceAddress;
private int servicePort;*/
public NettyClient(){
log.info("begin init NettyClient");
bootstrap=new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new RpcClientInitializer());
/* this.serviceAddress=serviceAddress;
this.servicePort=servicePort;*/
}
public void sendRequest(RpcProtocol<RpcRequest> protocol, IRegistryService registryService) throws Exception {
ServiceInfo serviceInfo=registryService.discovery(protocol.getContent().getClassName());
ChannelFuture future=bootstrap.connect(serviceInfo.getServiceAddress(),serviceInfo.getServicePort()).sync();
future.addListener(listener->{
if(future.isSuccess()){
log.info("connect rpc server {} success.",serviceInfo.getServiceAddress());
}else{
log.error("connect rpc server {} failed .",serviceInfo.getServiceAddress());
future.cause().printStackTrace();
eventLoopGroup.shutdownGracefully();
}
});
log.info("begin transfer data");
future.channel().writeAndFlush(protocol);
}
}
修改RpcInvokerProxy
将静态ip和地址,修改成IRegistryService
@Slf4j
public class RpcInvokerProxy implements InvocationHandler {
/* private String serviceAddress;
private int servicePort;*/
IRegistryService registryService;
public RpcInvokerProxy(IRegistryService registryService) {
/* this.serviceAddress = serviceAddress;
this.servicePort = servicePort;*/
this.registryService=registryService;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("begin invoke target server");
//组装参数
RpcProtocol<RpcRequest> protocol=new RpcProtocol<>();
long requestId= RequestHolder.REQUEST_ID.incrementAndGet();
Header header=new Header(RpcConstant.MAGIC, SerialType.JSON_SERIAL.code(), ReqType.REQUEST.code(),requestId,0);
protocol.setHeader(header);
RpcRequest request=new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameterTypes(method.getParameterTypes());
request.setParams(args);
protocol.setContent(request);
//发送请求
NettyClient nettyClient=new NettyClient();
//构建异步数据处理
RpcFuture<RpcResponse> future=new RpcFuture<>(new DefaultPromise<>(new DefaultEventLoop()));
RequestHolder.REQUEST_MAP.put(requestId,future);
nettyClient.sendRequest(protocol,this.registryService);
return future.getPromise().get().getData();
}
}
SpringRpcReferenceBean
修改引用bean,增加注册中心配置
public class SpringRpcReferenceBean implements FactoryBean<Object> {
private Class<?> interfaceClass;
private Object object;
/* private String serviceAddress;
private int servicePort;*/
//修改增加注册中心
private byte registryType;
private String registryAddress;
@Override
public Object getObject() throws Exception {
return object;
}
public void init(){
//修改增加注册中心
IRegistryService registryService= RegistryFactory.createRegistryService(this.registryAddress, RegistryType.findByCode(this.registryType));
this.object= Proxy.newProxyInstance(this.interfaceClass.getClassLoader(),
new Class<?>[]{this.interfaceClass},
new RpcInvokerProxy(registryService));
}
@Override
public Class<?> getObjectType() {
return this.interfaceClass;
}
public void setInterfaceClass(Class<?> interfaceClass) {
this.interfaceClass = interfaceClass;
}
/* public void setServiceAddress(String serviceAddress) {
this.serviceAddress = serviceAddress;
}
public void setServicePort(int servicePort) {
this.servicePort = servicePort;
}*/
public void setRegistryType(byte registryType) {
this.registryType = registryType;
}
public void setRegistryAddress(String registryAddress) {
this.registryAddress = registryAddress;
}
}
SpringRpcReferencePostProcessor
@Slf4j
public class SpringRpcReferencePostProcessor implements ApplicationContextAware, BeanClassLoaderAware, BeanFactoryPostProcessor {
private ApplicationContext context;
private ClassLoader classLoader;
private RpcClientProperties clientProperties;
public SpringRpcReferencePostProcessor(RpcClientProperties clientProperties) {
this.clientProperties = clientProperties;
}
//保存发布的引用bean信息
private final Map<String, BeanDefinition> rpcRefBeanDefinitions=new ConcurrentHashMap<>();
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader=classLoader;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context=applicationContext;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefinitionname:beanFactory.getBeanDefinitionNames()){
//遍历bean定义,然后获取到加载的bean,遍历这些bean中的字段,是否携带GpRemoteReference注解
//如果有,则需要构建一个动态代理实现
BeanDefinition beanDefinition=beanFactory.getBeanDefinition(beanDefinitionname);
String beanClassName=beanDefinition.getBeanClassName();
if(beanClassName!=null){
Class<?> clazz=ClassUtils.resolveClassName(beanClassName,this.classLoader);
ReflectionUtils.doWithFields(clazz,this::parseRpcReference);
}
}
//将@GpRemoteReference注解的bean,构建一个动态代理对象
BeanDefinitionRegistry registry=(BeanDefinitionRegistry)beanFactory;
this.rpcRefBeanDefinitions.forEach((beanName,beanDefinition)->{
if(context.containsBean(beanName)){
log.warn("SpringContext already register bean {}",beanName);
return;
}
registry.registerBeanDefinition(beanName,beanDefinition);
log.info("registered RpcReferenceBean {} success.",beanName);
});
}
private void parseRpcReference(Field field){
GpRemoteReference gpRemoteReference=AnnotationUtils.getAnnotation(field,GpRemoteReference.class);
if(gpRemoteReference!=null) {
BeanDefinitionBuilder builder=BeanDefinitionBuilder.genericBeanDefinition(SpringRpcReferenceBean.class);
builder.setInitMethodName(RpcConstant.INIT_METHOD_NAME);
builder.addPropertyValue("interfaceClass",field.getType());
/*builder.addPropertyValue("serviceAddress",clientProperties.getServiceAddress());
builder.addPropertyValue("servicePort",clientProperties.getServicePort());*/
builder.addPropertyValue("registryType",clientProperties.getRegistryType());
builder.addPropertyValue("registryAddress",clientProperties.getRegistryAddress());
BeanDefinition beanDefinition=builder.getBeanDefinition();
rpcRefBeanDefinitions.put(field.getName(),beanDefinition);
}
}
}
RpcRefernceAutoConfiguration
@Configuration
public class RpcRefernceAutoConfiguration implements EnvironmentAware{
@Bean
public SpringRpcReferencePostProcessor postProcessor(){
String address=environment.getProperty("gp.serviceAddress");
int port=Integer.parseInt(environment.getProperty("gp.servicePort"));
RpcClientProperties rc=new RpcClientProperties();
rc.setServiceAddress(address);
rc.setServicePort(port);
rc.setRegistryType(Byte.parseByte(environment.getProperty("gp.registryType")));
rc.setRegistryAddress(environment.getProperty("gp.registryAddress"));
return new SpringRpcReferencePostProcessor(rc);
}
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
this.environment=environment;
}
}
application.properties
修改netty-rpc-consumer模块中的配置
gp.serviceAddress=192.168.1.102
gp.servicePort=20880
gp.registryType=0
gp.registryAddress=192.168.221.128:2181
负载均衡的测试
增加一个服务端的启动类,并且修改端口。然后客户端不需要重启的情况下刷新浏览器,即可看到负载均衡的效果。

图7-5
需要源码的同学,请关注公众号[跟着Mic学架构],回复关键字[rpc],即可获得
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
Mic带你学架构!
如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注「跟着Mic学架构」公众号公众号获取更多技术干货!

[年薪60W分水岭]基于Netty手写Apache Dubbo(带注册中心和注解)的更多相关文章
- 基于Netty4手把手实现一个带注册中心和注解的Dubbo框架
阅读这篇文章之前,建议先阅读和这篇文章关联的内容. 1. 详细剖析分布式微服务架构下网络通信的底层实现原理(图解) 2. (年薪60W的技巧)工作了5年,你真的理解Netty以及为什么要用吗?(深度干 ...
- 基于netty手写RPC框架
代码目录结构 rpc-common存放公共类 rpc-interface为rpc调用方需要调用的接口 rpc-register提供服务的注册与发现 rpc-client为rpc调用方底层实现 rpc- ...
- 放弃antd table,基于React手写一个虚拟滚动的表格
缘起 标题有点夸张,并不是完全放弃antd-table,毕竟在react的生态圈里,对国人来说,比较好用的PC端组件库,也就antd了.即便经历了2018年圣诞彩蛋事件,antd的使用者也不仅不减,反 ...
- 手写实现RPC框架(不带注册中心和带注册中心两种)
实现自己的RPC框架如果不需要自定义协议的话那就要基于Socket+序列化. ProcessorHandler:主要是用来处理客户端的请求. package dgb.nospring.myrpc; i ...
- Tensorflow之基于MNIST手写识别的入门介绍
Tensorflow是当下AI热潮下,最为受欢迎的开源框架.无论是从Github上的fork数量还是star数量,还是从支持的语音,开发资料,社区活跃度等多方面,他当之为superstar. 在前面介 ...
- 基于vue手写tree插件那点事
目录 iview提供的控件 手写控件 手写控件扩展 手写控件总结 # 加入战队 微信公众号 主题 Tree树形控件在前端开发中必不可少,对于数据的展示现在网站大都采取树形展示.因为大数据全部展示出来对 ...
- 自己手写实现Dubbo
目录 dubbo 简单介绍 为什么手写实现一下bubbo? 什么是RPC? 接口抽象 服务端实现 注册中心 消费者端: dubbo 简单介绍 dubbo 是阿里巴巴开源的一款分布式rpc框架. 为什么 ...
- [手写系列] Spirit带你实现防抖函数和节流函数
前言 防抖函数和节流函数,无论是写业务的时候还是面试的时候,想必大家已经听过很多次了吧.但是大家在用到的时候,有了解过他们之间的区别嘛,他们是如何实现的呢?还是说只是简单的调用下像lodash和und ...
- zookeeper(5)--基于watcher原理实现带注册中心的RPC框架
一.带版本控制的注册中心RPC框架 server端 //注册中心接口 public interface IRegisterCenter { public void register(String se ...
随机推荐
- MySQL技术专题(X)该换换你的数据库版本了,让我们一同迎接8.0的到来哦!(初探篇)
前提背景 MySQL关是一种关系数据库管理系统,所使用的 SQL 语言是用于访问数据库的最常用的标准化语言,其特点为体积小.速度快.总体拥有成本低,尤其是开放源码这一特点,在 Web应用方面 MySQ ...
- 从零入门 Serverless | 架构的演进
作者 | 许晓斌 阿里云高级技术专家 本文整理自<Serverless 技术公开课>,关注"Serverless"公众号,回复 入门 ,即可获取 Serverless ...
- bzoj3262陌上花开 (CDQ,BIT)
题目大意 给定n朵花,每个花有三个属性,定义\(f[i]\)为满足\(a_j \le a_i\)且\(b_j \le b_i\)且\(c_j \le c_i\)的j的数量, 求\(d \in [0,n ...
- 升级更新 Windows10
升级更新 Windows10:获取 Windows 更新助手 升级 Windows10,它是先下载 Windows10 系统镜像,然后才升级.在下载完 Windows10 后,升级前,有一步骤会询问: ...
- 分布式全局ID与分布式事务
1. 概述 老话说的好:人不可貌相,海水不可斗量.以貌取人是非常不好的,我们要平等的对待每一个人. 言归正传,今天我们来聊一下分布式全局 ID 与分布式事务. 2. 分布式全局ID 2.1 分布式数据 ...
- 【UE4】GAMES101 图形学作业3:Blinn-Phong 模型与着色
总览 在这次编程任务中,我们会进一步模拟现代图形技术.我们在代码中添加了Object Loader(用于加载三维模型), Vertex Shader 与Fragment Shader,并且支持了纹理映 ...
- 第四次Alpha Scrum Meeting
本次会议为Alpha阶段第四次Scrum Meeting会议 会议概要 会议时间:2021年4月28日 会议地点:线上会议 会议时长:18min 会议内容简介:本次会议主要由每个人展示自己目前完成的工 ...
- BUAA_2020_软件工程_个人项目作业
作业抬头(1') 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 了解软件工程的技术,掌握工程化开发的能力 这 ...
- BUAA2020软工作业——提问回顾与个人总结
项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 提问回顾与个人总结 我在这个课程的目标是 进一步提高自己的编码能力,工程能力 这个作业在哪个具体方 ...
- 关于STM32 (Cortex-M3) 中NVIC的分析
一.STM32 (Cortex-M3) 中的优先级概念 STM32(Cortex-M3)中有两个优先级的概念:抢占式优先级和响应优先级,也把响应优先级称作"亚优先级"或" ...