Skip to main content

Java

本章节帮助文档致力使用 Java/GoLang 快速构建一个远程服务调用的过程。

Java 服务端

cn/fyupeng/Server.java
package cn.fyupeng;

import cn.fyupeng.annotation.ServiceScan;
import cn.fyupeng.enums.SerializerCode;
import cn.fyupeng.exception.RpcException;
import cn.fyupeng.net.netty.server.NettyServer;
import cn.fyupeng.serializer.CommonSerializer;

@ServiceScan
public class Server {
public static void main(String[] args) {
try {
NettyServer nettyServer = new NettyServer( "192.168.81.191", 9527, CommonSerializer.JSON_SERIALIZER);
nettyServer.start();
} catch (RpcException e) {
e.printStackTrace();
}
}
}
提示

如果有第三方容器,并且使用到注解依赖,建议切换实例对象的方式,只需要重写 newInstance 方法,从第三方容器获取即可。 如 Spring 管理容器的方式,这种方式不会再从 RNF 容器中再实例化对象,而是直接引用 Spring 的对象 Bean,放入 RNF 容器中直接管理。

NettyServer nettyServer = new NettyServer( "192.168.81.191", 9527, CommonSerializer.JSON_SERIALIZER) {
@Override
public Object newInstance(String fullName, String simpleName, String firstLowCaseName, Class<?> clazz) throws InstantiationException, IllegalAccessException {
return SpringContextUtil.getBean(firstLowCaseName, clazz);
}
};

Java 客户端

在上一篇有所介绍,创建代理来调用服务,能够增强客户端调用的功能,获取代理对象时,搭配使用注解 @Reference ,能够轻松使用它的几个属性:

#属性名默认值备注支持
1name""服务名,默认为全限定类名
2group""服务分组,默认为空字符串,Nacos 默认存储"DEFAULT_GROUP"
3retries2服务重试次数,客户端对同一个请求有容错重试的机会,直到重试达到阈值,在多节点分布式服务中使用更有优势。
4giveTime1服务重试让出时间,服务在一次调用失败后,并不会立即重试,设计的初衷是让所有线程都有机会获得CPU使用权,在高并发场景下能避免服务失败链锁反应。
5timeout3000服务超时时间,服务超时是一种逻辑上的处理,它允许客户端等待接收服务返回结果的逻辑最大时间,并不会限制服务真正执行的时间,它与服务异步时间搭配时间会有很明显的优势。
6asyncTime0服务异步时间,是相对于异步模式而言的,限制客户端等待服务返回结果的最大时间,阻塞模式下不限制等待最长时间。
提示

giveTimetimeoutasynTime 在使用时要根据具体的使用场景进行配置,才会有期望结果,在分布式环境中,一个服务超时后重试,在第二次调用时,如果第一个服务结果队列满,超时可以通过负载均衡策略到空闲节点,尝试从分布式缓存中获取结果(第一次请求已成功执行但未成功返回),获取失败则重新执行服务(第一次请求未执行成功,有可能导致一致性问题,服务第一次执行成功后,未能成功将结果保存在分布式到缓存中)

接下来我们开始开发我们的客户端代码,编写时推荐您下载源码帮助文档来开发。

package cn.fyupeng;

import cn.fyupeng.annotation.Reference;
import cn.fyupeng.factory.ThreadPoolFactory;
import cn.fyupeng.loadbalancer.RoundRobinLoadBalancer;
import cn.fyupeng.net.netty.client.NettyClient;
import cn.fyupeng.proxy.RpcClientProxy;
import cn.fyupeng.serializer.CommonSerializer;
import cn.fyupeng.service.HelloWorldService;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;

public class Client {
private static RoundRobinLoadBalancer randomLoadBalancer = new RoundRobinLoadBalancer();
// 使用 注册中心 方式
//private static NettyClient nettyClient = new NettyClient(randomLoadBalancer, CommonSerializer.HESSIAN_SERIALIZER);
// 使用 直连 方式
private static NettyClient nettyClient = new NettyClient("192.168.5.191", 9527, CommonSerializer.CJSON_SERIALIZER);
private static RpcClientProxy rpcClientProxy = new RpcClientProxy(nettyClient);
// @Reference(group = "1.0.1",timeout = 10000, asyncTime = -1)
@Reference(group = "1.0.1",timeout = 10000)
private static HelloWorldService service = rpcClientProxy.getProxy(HelloWorldService.class, Client.class);
//private static HelloWorldService service = rpcClientProxy.getJavassistProxy(HelloWorldService.class, Client.class);

private static ExecutorService pool = ThreadPoolFactory.createThreadPool(ThreadPoolFactory.CACHE_THREAD_POOL, "test_pool", 0);


public static void main(String[] args) throws InterruptedException {

String result = service.sayHello("this java request");
System.out.println(result);

}
}

除此以外,服务调用还需要编写服务接口和服务实现。

Java 服务接口

接口作为规范,服务端和客户端都必须实现,并且接口需要完成相同。

package cn.fyupeng.service;

public interface HelloWorldService {
String sayHello(String message);
}

Java 服务实现

#属性名默认值备注支持
1name""服务名,需要在使用注册中心负载时生效,使用直连不生效。默认为全限定类名
2group""服务分组,需要在使用注册中心负载时生效,使用直连不生效。默认为空字符串,将从注册中心 `Nacos` 获取默认存储"DEFAULT_GROUP"
package cn.fyupeng.service;

import cn.fyupeng.annotation.Service;

@Service(group = "1.0.0")
public class HelloWorldServiceImpl implements HelloWorldService {
@Override
public String sayHello(String message) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "这是java的sayHello远程服务,返回你发起的消息内容:" + message;
}
}