RPC-BDY(4)
-2022.7.19
前言
这一节的主要内容是将注册中心更改为nacos
原因:原有的框架服务端的地址固定,对于一个客户端,它只会去寻找那么一个服务提供者,如果这个提供者挂了或者换了地址,那就没有办法了。
实现nacos后,如果拿来的这个挂了,还可以重新请求,并且在这种情况下可以很方便地实现负载均衡。
一、nacos
1.简介:
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速 实现动态服务发现、服务配置、服务元数据及流量管理。
2.实现:
在RPC框架中,通过核心组件namingService来实现nacos的连接注册和获取
连接
//通过 NamingFactory 创建 NamingService 连接 Nacos
namingService = NamingFactory.createNamingService(SERVER_ADDR);
注册
namingService.registerInstance(serviceName, inetSocketAddress.getHostName(), inetSocketAddress.getPort());
获取
List<Instance> instances = namingService.getAllInstances(serviceName);
//这里为什么要get0
//通过 getAllInstance 获取到某个服务的所有提供者列表后,需要选择一个,这里就涉及了负载均衡策略,这里我们先选择第 0 个,后面某节会详细讲解负载均衡。
Instance instance = instances.get(0);
return new InetSocketAddress(instance.getIp(), instance.getPort());
二、nacos使用
- 运行(linux)
sh startup.sh -m standalone
- 引入依赖
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.0</version>
</dependency>
- 实现注册类
通过 NamingFactory 创建 NamingService 连接 Nacos并实现注册和发现
- ServiceRegistry注册接口
public interface ServiceRegistry {
void register(String serviceName, InetSocketAddress inetSocketAddress);
InetSocketAddress lookupService(String serviceName);
}
- NacosServiceRegistry注册实现类
/**
* Nacos服务注册中心
* @author bdy
*/
public class NacosServiceRegistry implements ServiceRegistry{
private static final Logger logger = LoggerFactory.getLogger(NacosServiceRegistry.class);
private static final String SERVER_ADDR = "127.0.0.1:8848";
private static final NamingService namingService;
static {
try {
//通过 NamingFactory 创建 NamingService 连接 Nacos
namingService = NamingFactory.createNamingService(SERVER_ADDR);
} catch (NacosException e) {
logger.error("连接到Nacos时有错误发生: ", e);
throw new RpcException(RpcError.FAILED_TO_CONNECT_TO_SERVICE_REGISTRY);
}
}
@Override
public void register(String serviceName, InetSocketAddress inetSocketAddress) {
try {
namingService.registerInstance(serviceName, inetSocketAddress.getHostName(), inetSocketAddress.getPort());
} catch (NacosException e) {
logger.error("注册服务时有错误发生:", e);
throw new RpcException(RpcError.REGISTER_SERVICE_FAILED);
}
}
@Override
public InetSocketAddress lookupService(String serviceName) {
try {
//获得提供某个服务的所有提供者的列表
List<Instance> instances = namingService.getAllInstances(serviceName);
//这里为什么要get0
//通过 getAllInstance 获取到某个服务的所有提供者列表后,需要选择一个,这里就涉及了负载均衡策略,这里我们先选择第 0 个,后面某节会详细讲解负载均衡。
Instance instance = instances.get(0);
return new InetSocketAddress(instance.getIp(), instance.getPort());
} catch (NacosException e) {
logger.error("获取服务时有错误发生:", e);
}
return null;
}
}
三、服务注册发现
1.RpcServer新增 publishService方法,用于向 Nacos 注册服务
<T> void publishService(Object service, Class<T> serviceClass);
2.NettyServer 的实现
将服务保存在本地的注册表,同时注册到 Nacos 上
@Override
public <T> void publishService(Object service, Class<T> serviceClass) {
serviceProvider.addService(service);
serviceRegistry.register(serviceClass.getCanonicalName(), new InetSocketAddress(host, port));
start();
}
3.NettyClient修改
在sendrequest方法中首先从 ServiceRegistry 中获取到服务的地址和端口,再构造
InetSocketAddress inetSocketAddress = serviceRegistry.lookupService(rpcRequest.getInterfaceName());
Channel channel = ChannelProvider.get(inetSocketAddress);
四、测试
1.NettyTestClient
/**
* 测试用Netty消费者
* @author bdy
*/
public class NettyTestClient {
public static void main(String[] args) {
RpcClient client = new NettyClient();
// client.setSerializer(new ProtobufSerializer());
RpcClientProxy rpcClientProxy = new RpcClientProxy(client);
HelloService helloService = rpcClientProxy.getProxy(HelloService.class);
HelloObject object = new HelloObject(12, "This is a message");
String res = helloService.hello(object);
System.out.println(res);
}
}
2.NettyTestServer
/**
* 测试用Netty服务提供者(服务端)
* @author bdy
*/
public class NettyTestServer {
public static void main(String[] args) {
HelloService helloService = new HelloServiceImpl();
NettyServer server = new NettyServer("127.0.0.1", 9999);
// server.setSerializer(new ProtobufSerializer());
server.publishService(helloService, HelloService.class);
}
}
总结
1.实现nacos注册中心
2.关于netty的编码解码器实现:
因为要传输的是自定义实体类,所以需要有编码解码器
pipeline.addLast(new ObjectDecoder(1024*1024, ClassResolvers.cacheDisabled((this.getClass().getClassLoader()))));
pipeline.addLast(new ObjectEncoder());
3.在NettyClient中可以不需要添加编解码器
原因:
在NettyClient中的bootstrap无用,主要是在ChannelProvider中实现bootstrap