preface
- K8S + ISTIO was used for service deployment and governance because of company business requirements, instead of the regular Springclould technology stack (including registry NACOS and OpenFeign remote service invocation).
- So I developed a RPC remote invocation service module based on AOP. The implementation is similar to feign in that it sends HTTP requests and returns results from proxy objects that invoke methods remotely.
The implementation code
- No more nonsense, the following directly on the code
- The following figure shows demo module division. Common is the common module, and demo-order and Demo-user simulate two service invocations.
- Define an annotation @rpcService that identifies the remote invocation class, similar to the @FeignClient annotation for feign.
/ * * * *@AUTHOR ZRH
* @DATE2021/4/10 * /
@Component
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RpcService {
/** * Remote service name */
String service(a);
/** * Port */
String port(a);
}
Copy the code
- Define two remote call interface request annotations @GET and @POST, equivalent to @PostMapping and @getMapping.
/ * * * *@AUTHOR ZRH
* @DATE2021/4/10 * /
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Post {
/** * Interface route **@return* /
String value(a);
}
/ * * * *@AUTHOR ZRH
* @DATE2021/4/10 * /
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Get {
/** * Interface route **@return* /
String value(a);
}
Copy the code
- Then define an AOP aspect handling class AopRpcHandler. Whenever there is an @POST or @GET annotation on the remote calling interface method, a proxy request will be made to the method.
- There is no need for the results of the original remote call method, so @around is used to circle the aspect. There is no need to execute the original method, so use JoinPoint as the interface. There are two more blocking methods, proceed, to get the result of the original agent method.
- The proxy object is used to obtain the parameter value, parameter name, interface routing address, interface request mode, remote service and port, etc. Use the OKHTTP utility class to send the proxy request and return the response.
/ * * *@AUTHOR ZRH
* @DATE2021/4/10 * /
@Slf4j
@Aspect
@Component
public class AopRpcHandler {
private final static String HTTP = "http://";
@Around(value = "@annotation(post)")
public String aopPost(JoinPoint joinPoint, Post post) {
String result = null;
String url = null;
try {
RpcService rpcService = (RpcService) joinPoint.getSignature().getDeclaringType().getAnnotation(RpcService.class);
url = HTTP + rpcService.service() + ":" + rpcService.port() + "/" + post.value();
Object[] args = joinPoint.getArgs();
result = OkHttpUtils.post(url, JSON.toJSONString(args[0]));
} catch (Throwable throwable) {
log.error("Service invocation exception, url = [{}]", url);
}
return result;
}
@Around(value = "@annotation(get)")
public String aopGet(JoinPoint joinPoint, Get get) {
String result = null;
String url = null;
try {
RpcService rpcService = (RpcService) joinPoint.getSignature().getDeclaringType().getAnnotation(RpcService.class);
url = HTTP + rpcService.service() + ":" + rpcService.port() + "/" + get.value();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Parameter[] parameters = signature.getMethod().getParameters();
if(parameters ! =null && parameters.length > 0) {
Object[] args = joinPoint.getArgs();
int length = parameters.length;
url += "?";
for (int i = 0; i < length; i++) {
url += parameters[i] + "=" + args[i];
if(i ! = length -1) {
url += "&";
}
}
}
result = OkHttpUtils.get(url);
} catch (Throwable throwable) {
log.error("Service invocation exception, url = [{}]", url);
}
returnresult; }}Copy the code
- Then create a remote call class in the Demo-User service if there is a remote call scenario. Use the @rpcService and @post annotations to state this. The return value and return type in the method can be customized, for example, there is a uniform response in the general project.
/ * * *@AUTHOR ZRH
* @DATE2021/4/10 1:06 * / 0010
@RpcService(service = "demo-order", port = "18002")
public class AopRpcDemo {
@Post("post")
public String post(String param) {
return "1"; }}Copy the code
- Use the same interface in the Demo-User service as you would normally invoke it.
/** * @AUTHOR ZRH * @DATE 2021/4/10 0010 0:42 */
@RestController
public class DemoController {
@Autowired
private AopRpcDemo aopRpcDemo;
@PostMapping("post")
public String post() {
String post =aopRpcDemo.post("zrh.post"); System.out.println(" call to remote interface method returns junction = ")+ post);
return"ok"; }}Copy the code
- If you start the Demo service like this, you can’t access it. Since the URL for HTTP requests in the AOP aspect processing class is concatenated not by domain names but by service names (typically, the sub-services of a microservice project may be clustered on different servers, so there will be multiple access domain names, etc.), if you simply concatenate multiple access domain names in the @rpcService annotation, Then you need to implement the load balancing policy to select a domain name for access)
- If the service invocation is based on the registry and feIGN, it is fine, because FEIGN will request the remote interface by the service name to the registry to find the address of the corresponding service. Since the registry is not used here, you need to add the address mapping on the hosts file on the window. Added the hosts file in C:\Windows\System32\drivers\etc. And flush the DNS contents in the CMD console using ipconfig/flushDNS.
- The service in @rpcService writes the service name instead of the service access domain name. This is because in multi-machine cluster deployment, Nginx load balancing can be used to forward requests by mapping the service name to the domain name, or K8S cluster environment can be used for service discovery and load balancing.
- On the internal network of the K8S cluster container, service names can be directly resolved to access other services, and load balancing is also implemented. External access Access requires the public domain name of the K8S cluster.
- Take a look at the configuration files for two services and the demo-Order interface
- Service starts, visit http://localhost:18001/post, the results are as follows:
- The final result was consistent with what we wanted.
- The demo above is a very simple implementation. If readers want to use this technology stack in their own projects, they need to consider whether service fault tolerance, service discovery, service flow limiting, and so on are compatible.
The last
- Openfeign can actually be used independently of SpringBoot. Start with the OpenFeign maven package
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.02.</version>
</dependency>
Copy the code
- When the @feignClient annotation is used to configure the access address of the interface to the URL, the result is the same as above.
/ * * *@AUTHOR ZRH
* @DATE2021/4/10 1:15 * / 0010
@FeignClient(name = "demo-user", url = "demo-user:18001")
public interface UserFeign {
@PostMapping("hello")
String hello(@RequestBody String param);
}
Copy the code
- I have uploaded the above code to my Gitee account: click Jump
- If there is anything wrong, please point it out and I will correct it.
- Study with an open mind and make progress together