gRPC结合nacos学习,实现远程调用grpc服务

gRPC结合nacos学习

1.1 基本概念

1.1.1 gRPC核心设计思路

对于远程调用的的设计思路,一般都是以下四个方面:

  1. 网络通信:gRPC 自己封装网络通信的部分,并提供多种语言的封装(C、Java[Netty]、GO).
  2. 协议:gRPC 使用 HTTP2 传输数据(二进制数据内容),支持双工(双向流)连接的多路复用.
  3. 序列化:基于 Protobuf 的序列化方式,时间效率和空间效率都是 JSON 的 3~5 倍.
  4. 代理的创建:让调用者像调用本地方法一样,去调用远端的服务方法.
1.1.2 为什么使用gRPC
  1. 高效:基于 HTTP2 协议,传输二进制数据内容,又基于 Protobuf 实现序列化,高效的进行进程间通信.
  2. 支持多种语言:原生支持 C、GO、Java 实现。C语言版本上可扩展 C++、C#、NodeJS、Python、Ruby、PHP.
  3. 跨平台:支持多平台运行 linux、Android、IOS、MacOS、Windows.

1.2 HTTP2.0 协议

  • 二进制通信:HTTP2.0 协议是一个二进制协议,效率高于 HTTP1.x,但是可读性较差.
  • 实现双工通信:服务器可对客户端进行消息推送.
  • 实现了多路复用机制:一个连接可以请求多个数据.

具体的通信过程,首先 HTTP2.0 协议以下三个重要概念:

  1. 数据流(Stream):客户端和服务器之间传输数据的通道(一个连接中可以有多个数据流).
  2. 消息(message):一个消息中就包含了多个帧.
  3. 帧(frame):就是一些具体的请求头,请求体信息.

1.3、Protocol Buffers(Protobuf)

1.3.1、protobuf 定义

protobuf 是一种与编程语言无关,与具体平台无关(任意操作系统)的序列化工具,自定义了中间语言(IDL),使得数据在 client 和 server 中进行 RPC 传输.

在这里插入图片描述

api的开发
  1. .proto文件 书写protobuf的IDL
  2. [了解]protoc命令 把proto文件中的IDL 转换成编程语言
    protoc --java_out=/xxx/xxx /xxx/xxx/xx.proto
syntax = "proto3";

option java_multiple_files = false;
option java_package = "com.grpc.hello";
option java_outer_classname = "HelloProto";

service HelloService {
  rpc hello(HelloRequest) returns(HelloResponse) {};
}

message HelloRequest {
  string name = 1;
}

message HelloResponse {
  string result = 1;
}
pom.xml
 <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.52.1</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.52.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.52.1</version>
        </dependency>
        <dependency> <!-- necessary for Java 9+ -->
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>6.0.53</version>
            <scope>provided</scope>
        </dependency>
 </dependencies>

<build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.7.1</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.21.7:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.52.1:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

service服务端开发
  1. 实现业务接口 添加具体的功能

  2. 创建服务端 (Netty)

	/*
        1.接受client提交的参数 request.getParameter()
        2.业务处理 service——dao 调用对应的业务功能
        3.提供返回值
     */
    @Override
    public void hello(com.grpc.hello.HelloProto.HelloRequest request,
                      io.grpc.stub.StreamObserver<com.grpc.hello.HelloProto.HelloResponse> responseObserver){
        //1.接受client的请求参数
        String name = request.getName();
        //2.业务处理
        System.out.println("name parameter"+name);
        //3.封装响应
        //3.1 创建相应对象的构造者
        HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();
        //3.2填充数据
        builder.setResult("hello method invoke ok");
        //3.3封装响应
        HelloProto.HelloResponse helloResponse = builder.build();

        
        //把数据传回
        responseObserver.onNext(helloResponse);
        //标记响应结束
        responseObserver.onCompleted();
    }


2.创建服务端 (Netty)
    public class GrpcServer1 {
    public static void main(String[] args) throws IOException, InterruptedException {

        //1.绑定接口
        ServerBuilder serverBuilder = ServerBuilder.forPort(9000);
        //2.发布服务
        serverBuilder.addService(new HelloServiceImpl());
//        serverBuilder.addService(new UserServiceImpl());
        //3.创建服务对象
        Server server = serverBuilder.build();

        server.start();
        server.awaitTermination();

    }
}
client客户端
  1. client通过代理对象完成远端对象的调用
//1 创建通信的管道
//2 获得代理对象 stub
public class GrpcClient1 {
    public static void main(String[] args) {
        //1.创建通信的管道
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();
        //2 获得代理对象 stub
        HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);
        //3.完成RPC调用
        //3.1准备参数
        HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
        builder.setName("world");
        HelloProto.HelloRequest request = builder.build();

        //3.2 进行功能rpc调用,获取相应内容
        HelloProto.HelloResponse helloResponse = helloService.hello(request);
        String result = helloResponse.getResult();
        System.out.println("result:"+result);
        
    }
}

注意事项

服务端 处理返回值时
responseObserver.onNext(helloResponse1);  //通过这个方法 把响应的消息 回传client
responseObserver.onCompleted();           //通知client 整个服务结束。底层返回标记 
                                          // client就会监听标记 【grpc做的】
                                          
requestObserver.onNext(helloRequest1);
requestObserver.onCompleted();

gRpc的四种通信方式

1. 简单rpc 一元rpc (Unary RPC)
2. 服务端流式RPC   (Server Streaming RPC)
3. 客户端流式RPC   (Client Streaming RPC)
4. 双向流RPC (Bi-directional Stream RPC)
  • 简单RPC(一元RPC)

    当client发起调用后,提交数据,并且等待 服务端响应。

    service HelloService{
      rpc hello(HelloRequest) returns (HelloResponse){}
      rpc hello1(HelloRequest1) returns (HelloResponse1){}
    }
    

在这里插入图片描述

  • 服务端流式RPC

    一个请求对象,服务端可以回传多个结果对象。

    返回加“stream”关键字

service HelloService{
  rpc hello(HelloRequest) returns (stream HelloResponse){}  //服务端流式rpc
  rpc hello1(HelloRequest1) returns (HelloResponse1){}   //一元rpc
}

在这里插入图片描述

服务端
public void c2ss(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {
        //1 接受client的请求参数
        String name = request.getName();
        //2 做业务处理
        System.out.println("name = " + name);
        //3 根据业务处理的结果,提供响应
        for (int i = 0; i < 9; i++) {
            HelloProto.HelloResponse.Builder builder = HelloProto.HelloResponse.newBuilder();
            builder.setResult("处理的结果 " + i);
            HelloProto.HelloResponse helloResponse = builder.build();

            responseObserver.onNext(helloResponse);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        responseObserver.onCompleted();
    }
客户端
public class GprcClient3 {
    public static void main(String[] args) {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();
        try {
            HelloServiceGrpc.HelloServiceBlockingStub helloService = HelloServiceGrpc.newBlockingStub(managedChannel);

            HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
            builder.setName("world");
            HelloProto.HelloRequest helloRequest = builder.build();
            这里要拿到服务端返回的所有信息 如果没拿到所有信息将会阻塞在这里 不可取!
            Iterator<HelloProto.HelloResponse> helloResponseIterator = helloService.c2ss(helloRequest);
            while (helloResponseIterator.hasNext()) {
                HelloProto.HelloResponse helloResponse = helloResponseIterator.next();
                System.out.println("helloResponse.getResult() = " + helloResponse.getResult());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            managedChannel.shutdown();
        }
    }
}

监听 异步方式 处理服务端流式RPC的开发
1. api
2. 服务端 
3. 客户端 
   public class GrpcClient4 {
    public static void main(String[] args) {
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9000).usePlaintext().build();

        try {

            HelloServiceGrpc.HelloServiceStub helloService = HelloServiceGrpc.newStub(managedChannel);

            HelloProto.HelloRequest.Builder builder = HelloProto.HelloRequest.newBuilder();
            builder.setName("xiaohei");
            HelloProto.HelloRequest helloRequest = builder.build();

            helloService.c2ss(helloRequest, new StreamObserver<HelloProto.HelloResponse>() {
                @Override
                public void onNext(HelloProto.HelloResponse value) {
                    //服务端 响应了 一个消息后,需要立即处理的话。把代码写在这个方法中。
                    System.out.println("服务端每一次响应的信息 " + value.getResult());
                }

                @Override
                public void onError(Throwable t) {

                }

                @Override
                public void onCompleted() {
                    //需要把服务端 响应的所有数据 拿到后,在进行业务处理。
                    System.out.println("服务端响应结束 后续可以根据需要 在这里统一处理服务端响应的所有内容");
                }
            });

            managedChannel.awaitTermination(12, TimeUnit.SECONDS);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            managedChannel.shutdown();
        }
    }
}
整合springboot

在这里插入图片描述

开发服务

1. 搭建SpringBoot的开发环境
   
2. 引入与Grpc相关的内容
    <dependency>
          <groupId>com.suns</groupId>
          <artifactId>rpc-grpc-api</artifactId>
          <version>1.0-SNAPSHOT</version>
     </dependency>

    <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-server-spring-boot-starter</artifactId>
        <version>2.14.0.RELEASE</version>
    </dependency>

开发服务

// 重复 多次 
@GrpcService
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
    @Override
    public void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {
        String name = request.getName();
        System.out.println("name is " + name);

        responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("this is result").build());
        responseObserver.onCompleted();
    }
}
// application.yml
# 核心配置的 就是gRPC服务的端口号
spring:
  application:
    name: boot-server

  main:
    web-application-type: none

grpc:
  server:
    port: 9000

客户端

 <dependency>
    <groupId>net.devh</groupId>
    <artifactId>grpc-client-spring-boot-starter</artifactId>
    <version>2.14.0.RELEASE</version>
 </dependency>

测试

1. yml
grpc:
  client:
    grpc-server:
      address: 'static://127.0.0.1:9000'
      negotiation-type: plaintext
      
2. 注入stub

@GrpcClient("grpc-server")
private HelloServiceGrpc.HelloServiceBlockingStub stub;

nacos:

目标

在 Nacos 服务器上注册多个相同的服务提供者实例。然后,服务消费者可以通过 Nacos 客户端来获取可用的服务实例列表,并选择其中一个进行请求。这样可以将请求分散到不同的服务实例上,从而实现负载均衡。

ter
2.14.0.RELEASE


测试

```java
1. yml
grpc:
  client:
    grpc-server:
      address: 'static://127.0.0.1:9000'
      negotiation-type: plaintext
      
2. 注入stub

@GrpcClient("grpc-server")
private HelloServiceGrpc.HelloServiceBlockingStub stub;

nacos:

目标

在 Nacos 服务器上注册多个相同的服务提供者实例。然后,服务消费者可以通过 Nacos 客户端来获取可用的服务实例列表,并选择其中一个进行请求。这样可以将请求分散到不同的服务实例上,从而实现负载均衡。

server:
application.yml

server:
  port: 8080
spring:
  application:
    name: boot-server
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.221.139:8848
        enabled: true
        ip: 127.0.0.1


grpc:
  server:
    port: 9001

grpc-boot-server: 服务端,提供服务
在这里插入图片描述

@GrpcService
@Slf4j
public class HelloServiceImpl extends HelloServiceGrpc.HelloServiceImplBase {
    private final DiscoveryClient discoveryClient;

    public HelloServiceImpl(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }


    @Override
    public void hello(HelloProto.HelloRequest request, StreamObserver<HelloProto.HelloResponse> responseObserver) {
        String name = request.getName();
        System.out.println("name is " + name);

        responseObserver.onNext(HelloProto.HelloResponse.newBuilder().setResult("this is result").build());
        responseObserver.onCompleted();
    }
}

grpc-boot-client:客户端 发起服务调用
在这里插入图片描述

server:
  port: 8081
spring:
  application:
    name: grpc-boot-client
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.221.139:8848
        enabled: true
        metadata:
          gRPC:
      server-addr: 192.168.221.139:8848


grpc:
  client:
    boot-server:
      negotiation-type: plaintext

application:启动类

@SpringBootApplication
@EnableDiscoveryClient
public class GrpcBootClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(GrpcBootClientApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

TestController:

@RestController
public class TestController {


    @GrpcClient("boot-server")
    private HelloServiceGrpc.HelloServiceBlockingStub stub;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private DiscoveryClient nacosDiscoveryClient;

    @Autowired
    private NacosServiceManager nacosServiceManager;

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @GetMapping("/getInstance")
    public Map<String, String> getServiceIpPort(String instanceName) {
        Map<String, String> serviceMap = new HashMap<String, String>();
        NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());
        Instance instance = null;
        try {
            instance = namingService.selectOneHealthyInstance(instanceName, nacosDiscoveryProperties.getGroup());
        } catch (NacosException e) {
            e.printStackTrace();
        }
        serviceMap.put("ip", instance.getIp());
        serviceMap.put("instanceId", instance.getInstanceId());
        return serviceMap;
    }


    @Autowired
    FactoryService factoryService;


    @RequestMapping("/test")
    public String test(String name) {

//        factoryService.getChannel();

        List<ServiceInstance> serviceInstances = nacosDiscoveryClient.getInstances("boot-server");

        if (ObjectUtils.isEmpty(serviceInstances)) {
            return "no server found";
        }
        ServiceInstance instance = serviceInstances.get(1); //或者使用负载均衡选择一个实例

//        ServiceInstance instance = loadBalancerClient.choose("boot-server");
        String host = instance.getHost();
        int port = Integer.valueOf(instance.getMetadata().get("gRPC_port"));
        // 创建GRPC通道和stub
//        final Channel channel = ((InProcessOrAlternativeChannelFactory) factoryService.getChannel()).createChannel("boot-server");

        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(managedChannel);

        // 调用GRPC服务
        HelloProto.HelloResponse helloResponse = stub.hello(HelloProto.HelloRequest.newBuilder().setName(name).build());
//        final HelloProto.HelloResponse helloResponse = GrpcUtil.hello();
        // 调用GRPC服务
        System.out.println(helloResponse.getResult());
        return helloResponse.getResult();
    }

    //不通过nacos的调用
    @RequestMapping("/test2")
    public String test2(String name) {
        System.out.println("name=" + name);
        HelloProto.HelloResponse helloResponse = stub.hello(HelloProto.HelloRequest.newBuilder().setName(name).build());
        System.out.println(helloResponse.getResult());
        return helloResponse.getResult();
    }


}

启动两个服务
在这里插入图片描述
在这里插入图片描述

查看nacos
在这里插入图片描述
在这里插入图片描述

直接访问TestController中的接口就可以调用成功啦

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值