1.說明
前文基于jsonrpc4j實現JSON-RPC over HTTP(服務端集成Spring Boot),
介紹了JSON-RPC over HTTP服務端的實現方法,
并且通過Postman工具調用服務端對外提供的方法,
下面介紹兩種基于Java代碼調用客戶端的方法:
- 非Spring框架的客戶端調用方法
- 基于Spring框架的客戶端調用方法
基于之前服務端的代碼開發示例,
下面詳細介紹這兩種方法的實現。
2.非Spring框架的客戶端
不依賴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 {/*** 基于方法名稱的客戶端調用*/@Testpublic 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*/@Testpublic 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方法。
3.基于Spring框架的客戶端
新建子工程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);@Beanpublic 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: 9091rpc:client:serverUrl: http://localhost:9090basePackage: 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);
}
4.基于Spring框架的客戶端測試
新建測試類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*/@Autowiredprivate BookRpcService bookRpcService;@Testpublic 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調用。
5.基于Spring框架的客戶端使用
類似于上面的測試類,
在業務代碼中使用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*/@Autowiredprivate BookRpcService bookRpcService;@PostConstructpublic 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)