1. Basic structure of RPC service framework
In essence, RPC is “calling remote methods like calling local methods”, which mainly involves data transmission between client and server. The overall RPC framework services are as follows:
2. Implement service components using HTTP
-
Registry: Zookeeper
-
Serialization: JSON
-
Network communication: HTTP protocol
-
Proxy: JDK dynamic proxy
3. Project structure
├ ─ RPC - HTTP - API │ ├ ─ SRC │ │ └ ─ the main │ │ ├ ─ Java │ │ │ └ ─ com │ │ │ └ ─ home │ │ │ └ ─ API │ │ └ ─ resources │ └ ─ target │ ├ ─ classes │ ├─ └ com │ ├─ home │ ├─ API │ ├─ gene-sources │ ├─rpc-http-consumer │ ├─ SRC │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ Java │ ├─ │ │ │ └ ─ com │ │ │ └ ─ home │ │ │ └ ─ consumer │ │ └ ─ resources │ └ ─ target │ ├ ─ classes │ │ └ ─ com │ │ └ ─ home │ │ └ ─ consumer │ └ ─ generated - sources │ └ ─ annotations ├ ─ RPC - HTTP - core │ ├ ─ SRC │ │ └ ─ the main │ │ ├ ─ Java │ │ │ ├ ─ client │ │ │ └ ─ com │ │ │ └ ─ home │ │ │ └ ─ core │ │ │ ├ ─ the entity │ │ │ └ ─ server │ │ └ ─ resources │ └ ─ target │ ├ ─ classes │ │ ├ ─ client │ │ └ ─ com │ │ └─home │ └─core │ ├─entity │ ├─ server │ ├─ generated-sources │ ├─ ─rpc-http-provider ├─ SRC │ ├─ main │ ├ ─ Java │ │ └ ─ com │ │ └ ─ home │ │ └ ─ the provider │ │ ├ ─ config │ │ └ ─ controller │ └ ─ resources └ ─ target ├ ─ classes │ └ ─ com │ └─home │ ├─config │ ├─ controller ├─ generated-sources ├─ annotationsCopy the code
① RPC service provider:
The service provider initializes the service when it is started. The node name created on ZooKeeper is “provider”, such as the class permission name +IP+ underscore (_) + port number.
@Slf4j
@SpringBootApplication
public class RpcHttpProviderApplication {
public static void main(String[] args) throws Exception {
// Initialize before startup
init();
SpringApplication.run(RpcHttpProviderApplication.class, args);
}
private static void init(a) throws Exception {
// ZooKeeper creates a node
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000.3);
CuratorFramework client = CuratorFrameworkFactory.builder().
connectString("localhost:2181").
namespace("rpc-http").
retryPolicy(retryPolicy)
.build();
client.start();
// set up the fully qualified name of the node of the UserService com.home.api.userservice
String userServiceName = "com.home.api.UserService";
registerService(client, userServiceName);
}
/** * Register the corresponding service node ** with ZooKeeper@param client
* @param serviceName
*/
private static void registerService(CuratorFramework client, String serviceName) throws Exception {
// User provider-related definitions
ServiceProviderDesc userServiceProviderDesc = ServiceProviderDesc.builder()
.host(InetAddress.getLocalHost().getHostAddress())
.port(8081)
.serviceClass(serviceName)
.build();
// Create a node
try {
if (null == client.checkExists().forPath("/" + serviceName)) {
client.create().withMode(CreateMode.PERSISTENT).forPath("/" + serviceName, "service".getBytes()); }}catch (Exception e) {
log.error("Provider node creation exception :{}", e.getMessage());
}
// Create a temporary node for the service
client.create().withMode(CreateMode.EPHEMERAL).
forPath( "/" + serviceName + "/" + userServiceProviderDesc.getHost() + "_" + userServiceProviderDesc.getPort(), "provider".getBytes()); }}Copy the code
② Consumers of RPC service:
The consumer of the service uses the HTTP request provider’s interface as a JDK proxy to retrieve JSON data and parse it:
/** * service consumers */
@Slf4j
@SpringBootApplication
public class RpcHttpConsumerApplication {
/** * The consumer side consumes */
public static void main(String[] args) {
UserService userService = RpcClient.create(UserService.class, "http://localhost:8081/");
User user = userService.findById(1);
log.info("find user id=1 from server: {}", user.getName()); }}Copy the code
The main RpcClient proxy classes:
/** * RPC client */
@Slf4j
public final class RpcClient {
static {
ParserConfig.getGlobalInstance().addAccept("com.home");
}
public static <T, filters> T createFromRegistry(final Class<T> serviceClass, final String zkUrl, Router router, LoadBalancer loadBalance, Filter filter) {
// curator Provider list from zk
List<String> invokers = new ArrayList<>();
// 1. Simple: Get the list of services provided from ZK
// 2. Challenge: Listen on zK's temporary nodes and update the list based on events (note that a global map is needed to keep the provider list for each service)
// router, loadbalance
List<String> urls = router.route(invokers);
String url = loadBalance.select(urls);
return (T) create(serviceClass, url, filter);
}
public static <T> T create(final Class<T> serviceClass, final String url, Filter... filters) {
// JDK dynamic proxy
return (T) Proxy.newProxyInstance(
RpcClient.class.getClassLoader(),
new Class[]{serviceClass},
new RpcInvocationHandler(serviceClass, url, filters)
);
}
public static class RpcInvocationHandler implements InvocationHandler {
public static final MediaType JSONTYPE = MediaType.get("application/json; charset=utf-8");
private finalClass<? > serviceClass;private final String url;
private final Filter[] filters;
public <T> RpcInvocationHandler(Class<T> serviceClass, String url, Filter... filters) {
this.serviceClass = serviceClass;
this.url = url;
this.filters = filters;
}
// You can try to write your own object serialization, binary or text, RPC is XML custom serialization, deserialization
// int byte char float double long bool
// [], data class
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
RpcRequest request = new RpcRequest();
request.setServiceClass(this.serviceClass.getName());
request.setMethod(method.getName());
request.setParams(params);
if (null! = filters) {for (Filter filter : filters) {
if(! filter.filter(request)) {return null;
}
}
}
RpcResponse response = post(request, url);
return JSON.parse(response.getResult().toString());
}
/** * Request the provider's interface to obtain relevant data information **@param req
* @param url
* @return
* @throws IOException
*/
private RpcResponse post(RpcRequest req, String url) throws IOException {
String reqJson = JSON.toJSONString(req);
log.info("req json: {}",reqJson);
// 1. The client can be reused
// 2. Try using httpClient or Netty Client
OkHttpClient client = new OkHttpClient();
final Request request = new Request.Builder()
.url(url)
.post(RequestBody.create(JSONTYPE, reqJson))
.build();
String respJson = client.newCall(request).execute().body().string();
log.info("resp json: {}",respJson);
returnJSON.parseObject(respJson, RpcResponse.class); }}}Copy the code
The request response results as follows:
[main] INFO client.RpcClient - req json: {"method":"findById"."params": [1]."serviceClass":"com.home.api.UserService"}
[main] INFO client.RpcClient - resp json: {"result":"{\" @ type \ ": \". Com. Home. The API User \ ", \ \ "id" : 1, \ "name \" : \ "RPC time: 2021-03-06 T12: but 697 \"}"."status":true."exception":null}
[main] INFO com.home.consumer.RpcHttpConsumerApplication - find user id=1 from server: rpc time: 2021-03-06T12:12:16.697
Copy the code
4. Project Address:
https://github.com/fengcharly/rpc-demo.git
Copy the code