记录一下,完成rpc协议的思路和代码
需要将HTTP请求接口改成成RPC协议(应用场景很小众,原因很简单,如果想要使用rpc,要对应的写一个工具类,将后端提供的接口对应的自动生成一份ts实现,一般互联网公司应该是不会使用)
RPC协议的主要目的是做到不同服务间调用方法像同一服务间调用本地方法一样
定义了一个类型别名 ServiceBuilder。这个类型别名是一个函数类型,它接受一个无参数的函数,返回类型是泛型 T,该泛型必须扩展自 RpcClientBase 类。可以将 ServiceBuilder 类型用作函数的类型注解,来描述一个返回 RpcClientBase 子类实例的函数
下面代码定义了一个名为 RpcProxy 的类,用作 RPC(远程过程调用)的代理。
这个类具有以下属性和方法:
_currentHost:私有属性,表示当前的主机地址。
_serviceCache:私有属性,用于缓存服务实例。
currentHostAddress:公共属性,用于获取当前主机地址。
setServerHost:公共方法,用于设置服务主机地址。
addInterceptor:公共方法,用于添加拦截器。
clearCache:公共方法,用于清空服务实例的缓存。
findService:公共方法,用于查找指定类型的服务实例。
在 RpcProxy 类中,可以看到 findService 方法通过调用 builder 函数来查找指定类型的服务实例。builder 函数是一个类型为 ServiceBuilder 的函数,接受一个无参数的函数,并返回类型为 T 的实例。T 必须扩展自 RpcClientBase 类。
在代码中,使用了一个示例 UserService 作为服务的代理设置。user 方法返回一个 UserService 实例,该实例的构造函数接受当前主机地址和一个空对象作为参数。
另外,还有一些辅助方法用于设置主机地址、添加拦截器和清空缓存。
整体而言,这个 RpcProxy 类是用于管理服务实例的代理类,它提供了一种简单的方式来获取指定类型的服务实例,并可以设置主机地址、添加拦截器等。这有助于在进行 RPC 调用时,提供代码的可读性和易用性。
/// rpc.ts 总的入口
import { Type } from 'typescript';
import { RpcInterceptor } from './interceptor';
import RpcClientBase from './client_base';
import {UserService} from './services/index'; /// 这边就需要工具类生成了
type ServiceBuilder<T extends RpcClientBase> = () => T;
class RpcProxy {
private _currentHost: string;
private _serviceCache: Map<Function, any>;
constructor(host?: string, ) {
this._currentHost = host || "unknown";
this._serviceCache = new Map();
}
get currentHostAddress(): string {
return this._currentHost;
}
/*模拟 服务代理设置 Start */
get user(): UserService {
return this.findService(() => new UserService(this.currentHostAddress,{}));
}
/* 服务代理设置 End 这边就是需要模板生成的部分*/
/// 设置服务主机地址
setServerHost(address: string): void {
console.log('RpcProxy setServerHost :' + address);
this._currentHost = address;
}
/// 添加拦截器(可补充)
addInterceptor(interceptor: RpcInterceptor): void {
jsonRpcInterceptHost.addInterceptor(interceptor);
}
/// 清空缓存
clearCache(): void {
this._serviceCache.clear();
}
/// 查找Service实例
findService<T extends RpcClientBase>(builder: ServiceBuilder<T>): T {
this.clearCache();
const serviceType = builder.constructor;
if (!this._serviceCache.has(serviceType)) {
this._serviceCache.set(serviceType, builder.call([]));
}
return this._serviceCache.get(serviceType) as T;
}
}
export default RpcProxy ;
拦截器
/// interceptor 拦截器
import RpcRequest from './request';
import {
RpcException,
RpcNetworkException,
RpcServerError
} from './exception';
export abstract class RpcInterceptor {
async onRequestTransmit(request: RpcRequest): Promise<any> {
return request;
}
async onResponse(response: Record<string, any>): Promise<Record<string, any>> {
return response;
}
async onResponseResult(result: any): Promise<any> {
return result;
}
async onResponseError(error: RpcServerError): Promise<RpcServerError> {
return error;
}
async onHttpRequestError(error: RpcNetworkException): Promise<RpcNetworkException> {
return error;
}
}
class _RpcInterceptHost implements RpcInterceptor {
private _interceptors: RpcInterceptor[] = [];
addInterceptor(interceptor: RpcInterceptor): void {
this._interceptors.push(interceptor);
}
async onRequestTransmit(request: RpcRequest): Promise<any> {
let pipeVal = request;
for (const interceptor of this._interceptors) {
pipeVal = await interceptor.onRequestTransmit(pipeVal);
}
return pipeVal;
}
async onResponse(response: Record<string, any>): Promise<Record<string, any>> {
let pipeVal = response;
for (const interceptor of this._interceptors) {
pipeVal = await interceptor.onResponse(pipeVal);
}
return pipeVal;
}
async onResponseError(error: RpcServerError): Promise<RpcServerError> {
let pipeVal = error;
if (pipeVal.code && (pipeVal.code == 1 )) {
return pipeVal;
}
for (const interceptor of this._interceptors) {
pipeVal = await interceptor.onResponseError(pipeVal);
}
return pipeVal;
}
async onHttpRequestError(error: RpcNetworkException): Promise<RpcNetworkException> {
let pipeVal = error;
if (pipeVal.code && (pipeVal.code == 1 || pipeVal.code == 2 || pipeVal.code == 3 || pipeVal.code == 9013)) {
return pipeVal;
}
for (const interceptor of this._interceptors) {
pipeVal = await interceptor.onHttpRequestError(pipeVal);
}
return pipeVal;
}
async onResponseResult(result: any): Promise<any> {
let pipeVal = result;
for (const interceptor of this._interceptors) {
pipeVal = await interceptor.onResponseResult(pipeVal);
}
return pipeVal;
}
}
export const rpcInterceptHost = new _RpcInterceptHost();
基础服务类
// client_base JsonRpcClientBase 服务的基类
import RpcRequest from './request';
import {
RpcException,
RpcNetworkException,
RpcServerError
} from './exception';
import {FHttpError} from './http_error';
import { rpcInterceptHost } from './interceptor';
import { jrpcHttpPool } from './http_pool';
class RpcClientBase {
static DEAULT_TIMEOUT: number = 3000;
private _timeout: number;
private _serviceName: string;
private readonly host: string;
private readonly headers: { [key: string]: string } | undefined;
constructor(
host: string,
serviceName: string,
{
headers,
timeout,
}: {
headers?: { [key: string]: string };
timeout?: number;
}
) {
this.host = host;
this.headers = headers;
this._timeout = timeout || RpcClientBase.DEAULT_TIMEOUT;
this._serviceName = serviceName;
}
get timeout(): number {
return this._timeout;
}
get serviceName(): string {
return this._serviceName;
}
get servicePath(): string {
return this.host.endsWith('/')
? `${this.host}${this.serviceName}`
: `${this.host}/${this.serviceName}`;
}
setServiceName(name: string): void {
this._serviceName = name;
}
protected async call(method: string, args?: any): Promise<any> {
const request = new RpcRequest(method, args);
return this._transmit(request);
}
protected notify(method: string, args?: any): void {
const request = new RpcRequest(method, args, );
this._transmit(request);
}
private async _transmit(request: RpcRequest): Promise<any> {
let result;
try {
const req = await rpcInterceptHost.onRequestTransmit(request);
const deepReq = req.to();
const response = await this._postRequest(deepReq);
if (!response) {
throw new RpcException({ message: 'Response Empty',name :'' });
}
result = await this._handleDecoded(response);
} catch (e) {
if (e instanceof FHttpError) {
await this._handleHttpRequestError(
new RpcNetworkException(e.toString(),'')
);
}
throw e;
}
return result;
}
private async _postRequest(deepReq: Record<string, any>): Promise<any> {
try {
const httpClient = jrpcHttpPool.getClient(
this.host,
this.timeout,
this.headers,
);
const response = await httpClient.post(`/${this.serviceName}`,deepReq );
if (response.status !== 200) {
throw new RpcException({
message: `Http error. Status code: ${response.status}.`,
name :''
});
}
return response.data;
} catch (e) {
throw new RpcException({ message: 'Http error', data: e ,name :''});
}
}
private async _handleHttpRequestError(
error: RpcNetworkException
): Promise<any> {
const res = await rpcInterceptHost.onHttpRequestError(error);
throw res;
}
private async _handleDecoded(response: { [key: string]: any }): Promise<any> {
const res = await rpcInterceptHost.onResponse(response);
if ('error' in res) {
let error = RpcServerError.from(res['error']);
error = await jsonRpcInterceptHost.onResponseError(error);
throw error;
}
let result = res['result'];
result = await jsonRpcInterceptHost.onResponseResult(result);
return result;
}
}
export default RpcClientBase;
这部分就是需要用代码生成的将后台接口全部实现成这样(就能轻轻松松调用rpc了)
/// 具体的实现 CreateUserRequest
import RpcClientBase from '../client_base'
import {CreateUserRequest,} from './user.m'
export class UserService extends RpcClientBase {
constructor(
host: string, {
serviceName = "IUserService",
headers,
timeout
}: {
serviceName?: string,
headers?: Record<string, string>,
timeout?: number
}){ super(
host,
serviceName,
{ headers,
timeout});
}
async createUserAsync(request: CreateUserRequest): Promise<String> {
var rpcRst = await this.call("CreateUserAsync", request);
return rpcRst;
}
}```
上面就是部分关键的代码 下面就是封装使用
```typescript
import RpcProxy from "./rpc";
import { RpcInterceptor } from "./interceptor";
import RpcRequest from "./request";
import { RpcServerError } from "./exception";
import { ElMessage, ElNotification } from "element-plus";
import i18n from "@/locales";
import router from "@/router";
export class ProxyHost {
private useMock: boolean = false;
private proxy: RpcProxy | null = null;
// JSON-RPC client
getProxy(): RpcProxy {
if (this.proxy === null) {
this.init();
}
return this.proxy!;
}
// 重启代理
//
// useMock 是否开启Mock
reboot(useMock: boolean = false): void {
this.useMock = useMock;
this.buildProxy();
}
// 初始化代理
private init(): void {
this.buildProxy();
this.listenServerHostChange();
}
// 构建代理实例
private buildProxy(): void {
const rpcProxy: RpcProxy = new RpcProxy();
this.proxy = this.useMock ? new RpcMockProxy({}) : rpcProxy;
this.setRpcHost(this.proxy!);
this.proxy!.addInterceptor(new DefaultInterceptor());
}
// 监听服务地址变更
private listenServerHostChange(): void {
this.setRpcHost(this.proxy!);
this.proxy!.clearCache();
}
private setRpcHost(rpc: RpcProxy): void {
rpc.setServerHost("/api");
}
}
class DefaultInterceptor extends RpcInterceptor {
private _networkRetryCount: number = 0;
public async onRequestTransmit(
request: RpcRequest
): Promise<any> {
console.log("%c Line:79 🎂 request", "color:#2eafb0", request);
return request;
}
public onResponse(
response: Record<string, any>
): Promise<Record<string, any>> {
return new Promise((resolve, reject) => {
if ("error" in response) {
const error = RpcServerError.from(response["error"]);
if (error instanceof RpcServerError) {
this.onResponseError(error);
return new Error();
}
}
resolve(response);
});
}
public async onResponseResult(result: any): Promise<any> {
return super.onResponseResult(result);
}
public async onResponseError(
error: RpcServerError
): Promise<RpcServerError> {
const errorCode = error.code;
try {
if (errorCode == 10086112)
{
ElMessage.error("账号已在其他设备登录");
router.replace({ path: "/login" });
}
else if (errorCode != null && (errorCode == 1 || errorCode == 2)) {
ElMessage.error("登录已过期");
router.replace({ path: "/login" });
} else {
const text: string | undefined = i18n.global.t("errorCode" + errorCode);
ElMessage.error(text ?? "Unkonwn RPC Exception");
}
} catch (e) {
ElNotification.error({
title: "提示",
message: "Unkonwn RPC Exception",
});
}
return error;
}
}
class RpcMockProxy extends RpcProxy {
constructor({ host }: { host?: string }) {
super(host);
}
}
export default new ProxyHost().getProxy();