前文基于jsonrpc4j实现JSON-RPC over HTTP(服务端集成Spring Boot),
介绍了JSON-RPC over HTTP服务端的实现方法,
并且通过Postman工具调用服务端对外提供的方法,
下面介绍两种基于Java代码调用客户端的方法:
基于之前服务端的代码开发示例,
下面详细介绍这两种方法的实现。
不依赖Spring框架的客户端,
实现SON-RPC over HTTP的RPC调用,
只需要依赖jsonrpc4j框架即可:
<dependency>
<groupId>com.github.briandilley.jsonrpc4j</groupId>
<artifactId>jsonrpc4j</artifactId>
<version>1.6</version>
</dependency>
新建客户端测试类JsonRpcClientTest.java如下:
package com.ai.json.rpc.client;
import java.net.URL;
import org.junit.jupiter.api.Test;
import com.ai.json.rpc.entity.Book;
import com.ai.json.rpc.service.BookRpcService;
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
import com.googlecode.jsonrpc4j.ProxyUtil;
/**
* 测试JSON-RPC over HTTP调用,客户端不依赖Spring Boot,服务端依赖Spring Boot
*
*/
public class JsonRpcClientTest {
/**
* 基于方法名称的客户端调用
*/
@Test
public void testClint2Server() throws Throwable {
URL url = new URL("http://localhost:9090/rpc/books");
JsonRpcHttpClient client = new JsonRpcHttpClient(url);
// 调用JSON RPC服务下的findById方法
Book book = client.invoke("findById", new Object[] { "1" }, Book.class);
System.out.println(book);
}
/**
* 基于API的客户端调用,需要支持JSON-RPC over HTTP
*/
@Test
public void testApi2Server() throws Throwable {
URL url = new URL("http://localhost:9090/rpc/books");
JsonRpcHttpClient client = new JsonRpcHttpClient(url);
// 创建客户端的JSON RPC服务的代理类
BookRpcService bookRpcService = ProxyUtil.createClientProxy(getClass().getClassLoader(), BookRpcService.class,
client);
// 基于API调用findById方法
Book book = bookRpcService.findById("2");
System.out.println(book);
}
}
这里通过JsonRpcHttpClient调用JSON-RPC对外提供的方法:
http://localhost:9090/rpc/books
可以直接通过JsonRpcHttpClient的invoke方法调用,
也可以通过ProxyUtil创建代理类,
通过接口API的方式优雅的调用RPC方法。
新建子工程JSON-RPC-SB-client,
pom增加依赖管理如下:
<dependencies>
<dependency>
<groupId>edu.yuwen.protocol</groupId>
<artifactId>JSON-RPC-SB-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
新建启动类JsonRpcClientApplication.java:
package com.ai.json.rpc.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JsonRpcClientApplication {
public static void main(String[] args) {
SpringApplication.run(JsonRpcClientApplication.class, args);
}
}
新建配置类RpcConfiguration.java:
package com.ai.json.rpc.client.config;
import java.net.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.googlecode.jsonrpc4j.spring.AutoJsonRpcClientProxyCreator;
@Configuration
public class RpcConfiguration {
private static Logger LOG = LoggerFactory.getLogger(RpcConfiguration.class);
@Bean
public AutoJsonRpcClientProxyCreator rpcClientProxyCreator(@Value("${rpc.client.serverUrl}") String url,
@Value("${rpc.client.basePackage}") String basePackage) {
LOG.info("配置远程服务端的URL={}, 本地扫描包路径basePackage={}", url, basePackage);
AutoJsonRpcClientProxyCreator clientProxyCreator = new AutoJsonRpcClientProxyCreator();
try {
clientProxyCreator.setBaseUrl(new URL(url));
clientProxyCreator.setContentType("application/json");
} catch (Exception e) {
LOG.error("配置AutoJsonRpcClientProxyCreator发生异常=", e);
}
clientProxyCreator.setScanPackage(basePackage);
return clientProxyCreator;
}
}
新建配置文件application.yml:
server:
port: 9091
rpc:
client:
serverUrl: http://localhost:9090
basePackage: com.ai.json.rpc.service
注意这里依赖了JSON-RPC-SB-api模块,
涉及到的关键代码如下:
package com.ai.json.rpc.service;
import com.ai.json.rpc.entity.Book;
import com.googlecode.jsonrpc4j.JsonRpcService;
@JsonRpcService("rpc/books")
public interface BookRpcService {
public Book findById(String id);
}
新建测试类JsonRpcContainerTest.java:
package com.ai.json.rpc.client;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.ai.json.rpc.entity.Book;
import com.ai.json.rpc.service.BookRpcService;
/**
* 测试JSON-RPC over HTTP调用,客户端和服务端都依赖Spring Boot容器
*
*/
@SpringBootTest
public class JsonRpcContainerTest {
private static Logger LOG = LoggerFactory.getLogger(JsonRpcContainerTest.class);
/**
* Spring容器注入的rpc服务中已经设置好了远端服务URL
*/
@Autowired
private BookRpcService bookRpcService;
@Test
public void queryBooks() {
LOG.info("客户端开始自动查询图书");
for (int i = 1; i <= 3; i++) {
String id = String.valueOf(i);
Book book = bookRpcService.findById(id);
LOG.info("根据图书Id={}找到图书Book={}", id, book);
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
LOG.error("自动查询图书发生异常=", e);
}
}
}
}
通过@SpringBootTest注解启动容器,
可以获得代理过后的BookRpcService实例,
直接调用接口的API即可实现RPC调用。
类似于上面的测试类,
在业务代码中使用JSON-RPC over HTTP 接口时,
直接注入BookRpcService即可使用,
运行JsonRpcClientApplication启动类后,
会自动注入实际的动态代理类。
新建业务类AutoQueryBookService.java:
package com.ai.json.rpc.client.auto;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.ai.json.rpc.entity.Book;
import com.ai.json.rpc.service.BookRpcService;
@Component
public class AutoQueryBookService {
private static Logger LOG = LoggerFactory.getLogger(AutoQueryBookService.class);
/**
* Spring容器注入的rpc服务中已经设置好了远端服务URL
*/
@Autowired
private BookRpcService bookRpcService;
@PostConstruct
public void queryBooks() {
LOG.info("客户端开始自动查询图书");
for (int i = 1; i <= 3; i++) {
String id = String.valueOf(i);
Book book = bookRpcService.findById(id);
LOG.info("根据图书Id={}找到图书Book={}", id, book);
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
LOG.error("自动查询图书发生异常=", e);
}
}
}
}
这里通过@PostConstruct注解,
实现了再容器启动后,
在容器注入BookRpcService实例后,
自动开始执行查询图书的操作,
运行日志如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.1.RELEASE)
2023-12-11 15:58:24.045 INFO 13568 --- [ main] c.a.j.r.client.JsonRpcClientApplication : Starting JsonRpcClientApplication on yuwen-asiainfo with PID 13568 (D:\Code\Learn\protocol-impl\RPC-impl\JSON-RPC-impl\JSON-RPC-SpringBoot\JSON-RPC-SB-client\target\classes started by yuwen in D:\Code\Learn\protocol-impl\RPC-impl\JSON-RPC-impl\JSON-RPC-SpringBoot\JSON-RPC-SB-client)
2023-12-11 15:58:24.047 INFO 13568 --- [ main] c.a.j.r.client.JsonRpcClientApplication : No active profile set, falling back to default profiles: default
2023-12-11 15:58:24.546 INFO 13568 --- [ main] o.s.c.a.ConfigurationClassEnhancer : @Bean method RpcConfiguration.rpcClientProxyCreator is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.
2023-12-11 15:58:24.552 INFO 13568 --- [ main] c.a.j.r.client.config.RpcConfiguration : 配置远程服务端的URL=http://localhost:9090, 本地扫描包路径basePackage=com.ai.json.rpc.service
2023-12-11 15:58:25.062 INFO 13568 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9091 (http)
2023-12-11 15:58:25.071 INFO 13568 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-12-11 15:58:25.071 INFO 13568 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.36]
2023-12-11 15:58:25.134 INFO 13568 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-12-11 15:58:25.134 INFO 13568 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1054 ms
2023-12-11 15:58:25.170 INFO 13568 --- [ main] c.a.j.r.c.auto.AutoQueryBookService : 客户端开始自动查询图书
2023-12-11 15:58:25.358 INFO 13568 --- [ main] c.a.j.r.c.auto.AutoQueryBookService : 根据图书Id=1找到图书Book=Book [id=1, name=Java核心技术, price=199]
2023-12-11 15:58:35.362 INFO 13568 --- [ main] c.a.j.r.c.auto.AutoQueryBookService : 根据图书Id=2找到图书Book=Book [id=2, name=人月神话, price=58]
2023-12-11 15:58:45.365 INFO 13568 --- [ main] c.a.j.r.c.auto.AutoQueryBookService : 根据图书Id=3找到图书Book=Book [id=3, name=程序员养生指南, price=996]
2023-12-11 15:58:55.411 INFO 13568 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2023-12-11 15:58:55.518 INFO 13568 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9091 (http) with context path ''
2023-12-11 15:58:55.524 INFO 13568 --- [ main] c.a.j.r.client.JsonRpcClientApplication : Started JsonRpcClientApplication in 31.81 seconds (JVM running for 32.165)