java版gRPC实战之六:客户端动态获取服务端地址,2021Java开发社招面试解答之性能优化

java版gRPC实战之六:客户端动态获取服务端地址,2021Java开发社招面试解答之性能优化,第1张

java版gRPC实战之六:客户端动态获取服务端地址,2021Java开发社招面试解答之性能优化

import static com.google.common.base.Charsets.UTF_8;

@Component(“stubWrapper”)

@Data

@Slf4j

@ConfigurationProperties(prefix = “grpc”)

public class StubWrapper {

private static final String GRPC_SERVER_INFO_KEY = “/grpc/local-server”;

private String etcdendpoints;

private SimpleGrpc.SimpleBlockingStub simpleBlockingStub;

public String[] getGrpcServerInfo() {

// 创建client类

KV kvClient = Client.builder().endpoints(etcdendpoints.split(",")).build().getKVClient();

GetResponse response = null;

// 去etcd查询/grpc/local-server这个key的值

try {

response = kvClient.get(ByteSequence.from(GRPC_SERVER_INFO_KEY, UTF_8)).get();

} catch (Exception exception) {

log.error(“get grpc key from etcd error”, exception);

}

if (null==response || response.getKvs().isEmpty()) {

log.error(“empty value of key [{}]”, GRPC_SERVER_INFO_KEY);

return null;

}

// 从response中取得值

String rawAddrInfo = response.getKvs().get(0).getValue().toString(UTF_8);

// rawAddrInfo是“192.169.0.1:8080”这样的字符串,即一个IP和一个端口,用":"分割,

// 这里用":"分割成数组返回

return null==rawAddrInfo ? null : rawAddrInfo.split(";

}

@PostConstruct

public void simpleBlockingStub() {

// 从etcd获取地址信息

String[] array = getGrpcServerInfo();

log.info(“create stub bean, array info from etcd {}”, Arrays.toString(array));

// 数组的第一个元素是gRPC服务端的IP地址,第二个元素是端口

if (null==array || array.length<2) {

log.error(“can not get valid grpc address from etcd”);

return;

}

// 数组的第一个元素是gRPC服务端的IP地址

String addr = array[0];

// 数组的第二个元素是端口

int port = Integer.parseInt(array[1]);

// 根据刚才获取的gRPC服务端的地址和端口,创建channel

Channel channel = ManagedChannelBuilder

.forAddress(addr, port)

.usePlaintext()

.build();

// 根据channel创建stub

simpleBlockingStub = SimpleGrpc.newBlockingStub(channel);

}

}

  • GrpcClientService是封装了StubWrapper的服务类:

package com.bolingcavalry.dynamicrpcaddr;

import com.bolingcavalry.grpctutorials.lib.HelloReply;

import com.bolingcavalry.grpctutorials.lib.HelloRequest;

import com.bolingcavalry.grpctutorials.lib.SimpleGrpc;

import io.grpc.StatusRuntimeException;

import lombok.Setter;

import net.devh.boot.grpc.client.inject.GrpcClient;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service

public class GrpcClientService {

@Autowired(required = false)

@Setter

private StubWrapper stubWrapper;

public String sendMessage(final String name) {

// 很有可能simpleStub对象为null

if (null==stubWrapper) {

return “invalid SimpleBlockingStub, please check etcd configuration”;

}

try {

final HelloReply response = stubWrapper.getSimpleBlockingStub().sayHello(HelloRequest.newBuilder().setName(name).build());

return response.getMessage();

} ca

【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】

浏览器打开:qq.cn.hn/FTf 免费领取

tch (final StatusRuntimeException e) {

return "FAILED with " + e.getStatus().getCode().name();

}

}

}

  • 新增一个controller类GrpcClientController,提供一个http接口,里面会调用GrpcClientService的方法,最终完成远程gRPC调用:

package com.bolingcavalry.dynamicrpcaddr;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class GrpcClientController {

@Autowired

private GrpcClientService grpcClientService;

@RequestMapping("/")

public String printMessage(@RequestParam(defaultValue = “will”) String name) {

return grpcClientService.sendMessage(name);

}

}

  • 接下来新增一个controller类RefreshStubInstanceController,对外提供一个http接口refreshstub,作用是删掉stubWrapper这个bean,再重新注册一次,这样每当外部调用refreshstub接口,就可以从etcd取得服务端信息再重新实例化SimpleBlockingStub成员变量,这样就达到了客户端动态获取服务端地址的效果:

package com.bolingcavalry.dynamicrpcaddr;

import com.bolingcavalry.grpctutorials.lib.SimpleGrpc;

import org.springframework.beans.BeansException;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.support.AbstractBeanDefinition;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;

import org.springframework.context.ApplicationContext;

import org.springframework.context.ApplicationContextAware;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

@RestController

public class RefreshStubInstanceController implements ApplicationContextAware {

private ApplicationContext applicationContext;

@Autowired

private GrpcClientService grpcClientService;

@Override

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

this.applicationContext = applicationContext;

}

@RequestMapping("/refreshstub")

public String refreshstub() {

String beanName = “stubWrapper”;

//获取BeanFactory

DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();

// 删除已有bean

defaultListableBeanFactory.removeBeanDefinition(beanName);

//创建bean信息.

BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(StubWrapper.class);

//动态注册bean.

defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinitionBuilder.getBeanDefinition());

// 更新引用关系(注意,applicationContext.getBean方法很重要,会触发StubWrapper实例化 *** 作)

grpcClientService.setStubWrapper(applicationContext.getBean(StubWrapper.class));

return “Refresh success”;

}

}

  • 编码完成,开始验证;
部署gRPC服务端应用

部署gRPC服务端应用很简单,启动local-server应用即可:

部署etcd
  • 为了简化 *** 作,我这里的etcd集群是用docker部署的,对应的docker-compose.yml文件内容如下:

version: ‘3’

services:

etcd1:

image: “quay.io/coreos/etcd:v3.4.7”

entrypoint: /usr/local/bin/etcd

command:

  • ‘–name=etcd1’

  • ‘–data-dir=/etcd_data’

  • ‘–initial-advertise-peer-urls=http://etcd1:2380’

  • ‘–listen-peer-urls=http://0.0.0.0:2380’

  • ‘–listen-client-urls=http://0.0.0.0:2379’

  • ‘–advertise-client-urls=http://etcd1:2379’

  • ‘–initial-cluster-token=etcd-cluster’

  • ‘–heartbeat-interval=250’

  • ‘–election-timeout=1250’

  • ‘–initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380’

  • ‘–initial-cluster-state=new’

ports:

  • 2379:2379

volumes:

  • ./store/etcd1/data:/etcd_data

etcd2:

image: “quay.io/coreos/etcd:v3.4.7”

entrypoint: /usr/local/bin/etcd

command:

  • ‘–name=etcd2’

  • ‘–data-dir=/etcd_data’

  • ‘–initial-advertise-peer-urls=http://etcd2:2380’

  • ‘–listen-peer-urls=http://0.0.0.0:2380’

  • ‘–listen-client-urls=http://0.0.0.0:2379’

  • ‘–advertise-client-urls=http://etcd2:2379’

  • ‘–initial-cluster-token=etcd-cluster’

  • ‘–heartbeat-interval=250’

  • ‘–election-timeout=1250’

  • ‘–initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380’

  • ‘–initial-cluster-state=new’

ports:

  • 2380:2379

volumes:

  • ./store/etcd2/data:/etcd_data

etcd3:

image: “quay.io/coreos/etcd:v3.4.7”

entrypoint: /usr/local/bin/etcd

command:

  • ‘–name=etcd3’

  • ‘–data-dir=/etcd_data’

  • ‘–initial-advertise-peer-urls=http://etcd3:2380’

  • ‘–listen-peer-urls=http://0.0.0.0:2380’

  • ‘–listen-client-urls=http://0.0.0.0:2379’

  • ‘–advertise-client-urls=http://etcd3:2379’

  • ‘–initial-cluster-token=etcd-cluster’

  • ‘–heartbeat-interval=250’

  • ‘–election-timeout=1250’

  • ‘–initial-cluster=etcd1=http://etcd1:2380,etcd2=http://etcd2:2380,etcd3=http://etcd3:2380’

  • ‘–initial-cluster-state=new’

ports:

  • 2381:2379

volumes:

  • ./store/etcd3/data:/etcd_data

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5138731.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-17
下一篇 2022-11-17

发表评论

登录后才能评论

评论列表(0条)

保存