-
Notifications
You must be signed in to change notification settings - Fork 282
Home
在百度内部,常用语言(C++,Java,Go,PHP)的RPC框架都有多种实现,RPC框架这种基础软件不可或缺,但都很难讲出业务or晋升的价值,所以大部分RPC框架都充满了个人色彩,很难坚持到发展成拥有一个团队or社区持续打磨成为一个特别完善的产品,随着主力作者的离职,很多RPC框架也就留在了代码库的角落。即便如此,在百度缺乏组织支持的情况,全凭戈神一己之力也诞生了brpc这样的作品,brpc框架在百度内部成为C++技术栈的事实性标准,实在令人敬佩。
回到Java技术栈,我们在2010年就已经开始自研RPC框架,彼时业界也并没有一款特别能打的选择,但随着时间流逝,Dubbo发展迅速也成为了Java技术栈的首选RPC框架,但遗憾的是时至今日,可能由于某种竞争关系,Dubbo仍然不支持brpc协议,brpc也不支持dubbo协议。因此在百度内部为实现Java系统和C++系统的通讯,于2017年发起了brpc-java项目对标Dubbo,希望成为brpc生态下面的Java框架首选,但正如戈神所说,RPC框架如果作为产品其实不应该只侧重性能而应该更侧重效率,真正帮助用户屏蔽底层系统的各种奇妙问题,可以低成本的正确使用。很遗憾主打高性能的brpc-java没能发展成Dubbo这样的项目,受限于资源局限,随着主力作者的离职,brpc-java也注定只能消失在代码库的角落。
受到戈神的启发,在Java技术栈,SpringBoot&SpringCloud才是对业务发展最有效率的应用框架,我们不应该另立门户搞一套类似Dubbo的全栈框架(资源限制也不允许),而应该在Spring生态下寻求解决方案,因此在2019年又发起了一个有全新定位的RPC框架项目,代号为Starlight,Starlight定位是一个兼容SpringBoot&SpringCloud生态的点对点通信框架,Starlight提供单端口兼容Spring MVC http rest协议和brpc二进制协议的能力,使用Spring MVC框架的用户可以较为容易的迁移过来,同时也无需protobuf的编译过程(当然如果非常需要proto文件来编译也是可以用的)实现对brpc C++ Server&Client的通信能力,其他的服务注册发现、配置管理、负载均衡、熔断限流完全依赖Spring Cloud生态的各种Starter,也可以灵活借助Service Mesh的能力实现服务注册发现过程,无需特别的兼容适配。
在百度商业体系,Starlight已经完成对存量各种Java RPC框架的替换迁移,规模达到2w+容器,日pv达到200亿+。同时由于brpc已经捐赠给Apache基金会,早前的brpc-java无人维护并不适合加入brpc生态,Starlight在百度内部已经是brpc-java替代产品,为了避免商标问题,Starlight顺势开源,brpc-java开源仓库也更名为Starlight,之前的代码保存在brpc-java-v3分支,希望外部brpc-java用户有多一种选择。
Starlight 是一套面向云原生的微服务通信框架,兼容Spring生态,基于此可快速构建高效、稳定、可控、可观测的微服务应用,获得研发效率提升、业务稳定性增强等舒适体验。 核心特性如下:
- 多种协议支持:Starlight单端口支持brpc、stargate、spring mvc rest(http)协议,提供超丰富的使用场景
- 高性能远程通信:Starlight基于多路复用的NIO框架封装底层通性能力,提供高性能高并发网络通信能力
- 易于使用:无需处理protobuf编译过程,通过原生Java接口和POJO对象加上类级别的注解,类似Java RMI和Spring MVC使用体验,即可实现brpc二进制协议的Server和Client;支持无损升级、异常实例摘除;规范化的日志可以秒级定位超时问题、序列化失败问题
Java POJO类定义调用API。用于Java语言间调用,以及跨语言proto文件可手动翻译为Java接口和POJO对象。
- 添加maven依赖
<dependency>
<groupId>com.baidu.cloud</groupId>
<artifactId>starlight-all</artifactId>
<version>${最新版本}</version>
</dependency>
- 为服务端和消费端创建公共接口
跨语言场景按照proto文件定义翻译为Java接口与POJO定义
src/main/java/com/baidu/cloud/demo/api/UserService.java
public interface UserService {
User getUser(Long userId);
}
- 服务端编写业务逻辑并启动
编写业务逻辑
src/main/java/com/baidu/cloud/demo/provider/service/UserServiceImpl.java
public class UserServiceImpl implements UserService {
@Override
public User getUser(Long userId) {
User user = new User();
user.setUserId(userId);
user.setUserName("User1");
user.setBalance(1000.21d);
user.setGender(Gender.MALE);
List<String> tags = new LinkedList<>();
tags.add("fgh");
tags.add("123123");
user.setTags(tags);
List<ExtInfo> extInfos = new LinkedList<>();
ExtInfo extInfo = new ExtInfo("hobby", "learn");
extInfos.add(extInfo);
user.setExtInfos(extInfos);
user.setMap(Collections.singletonMap("key", new Address("Beijing")));
return user;
}
}
启动server
src/main/java/com/baidu/cloud/demo/provider/DemoProviderApp.java
public class DemoProviderApp {
public static void main(String[] args) {
// 初始化server
TransportConfig transportConfig = new TransportConfig();
StarlightServer starlightServer = new DefaultStarlightServer("localhost", 8005, transportConfig);
starlightServer.init();
// 暴露接口信息
ServiceConfig serviceConfig = new ServiceConfig();
starlightServer.export(UserService.class, new UserServiceImpl(), serviceConfig);
// 开启服务
starlightServer.serve();
synchronized (DemoProviderApp.class) {
try {
DemoProviderApp.class.wait();
} catch (Throwable e) {
}
}
}
}
- 客户端创建并发起调用
src/main/java/com/baidu/cloud/demo/consumer/DemoConsumerApp.java
public class DemoConsumerApp {
public static void main(String[] args) {
// 创建Client
TransportConfig config = new TransportConfig(); // 传输配置
StarlightClient starlightClient = new SingleStarlightClient("localhost", 8005, config);
starlightClient.init();
// 服务配置
ServiceConfig clientConfig = new ServiceConfig(); // 服务配置
clientConfig.setProtocol("brpc");
// clientConfig.setServiceId("demo.EchoService"); // 跨语言时指定服务端定义的serviceName
// 生成代理
JDKProxyFactory proxyFactory = new JDKProxyFactory();
UserService userService = proxyFactory.getProxy(UserService.class, clientConfig, starlightClient);
// 发起调用
User user = userService.getUser(1L);
System.out.println(user.toString());
// 销毁client
starlightClient.destroy();
System.exit(0);
}
}
编译proto文件为调用API。特用于客户端跨语言调用时,proto文件复杂无法手动翻译为POJO类。
- 添加maven依赖
按照如下添加并配置protoc-jar-maven-plugin,执行maven compile将proto文件编译为Java类。
<dependency>
<groupId>com.baidu.cloud</groupId>
<artifactId>starlight-all</artifactId>
<version>${最新版本}</version>
</dependency>
<build>
<plugins>
<!--编译proto文件为Java对象-->
<plugin>
<groupId>com.github.os72</groupId>
<artifactId>protoc-jar-maven-plugin</artifactId>
<version>3.6.0.1</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<!--仅支持pb2.5-->
<protocVersion>2.5.0</protocVersion>
<addSources>none</addSources>
<includeStdTypes>true</includeStdTypes>
<!--proto文件编译生成的Java类存放位置-->
<outputDirectory>src/main/java</outputDirectory>
<inputDirectories>
<!--proto文件存放位置-->
<include>src/main/proto</include>
</inputDirectories>
</configuration>
</execution>
<execution>
<id>generate-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<protocVersion>2.5.0</protocVersion>
<includeStdTypes>true</includeStdTypes>
<addSources>none</addSources>
<outputDirectory>src/test/java</outputDirectory>
<inputDirectories>
<include>src/test/proto</include>
</inputDirectories>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
- 客户端创建公共接口
客户端依照proto文件手写Java接口作为与服务端交互的接口,methodName需与proto文件中一致,接口名不做要求,参数与返回值为上步生成的Java类。
public interface UserProtoService {
// UserPb.UserProto为proto编译插件生成的类
// Echo与proto文件定义的方法名一致
UserPb.UserProto Echo(UserPb.UserProto user);
}
- 客户端创建并发起调用
public class BrpcProtoConsumer {
public static void main(String[] args) {
// 创建Client
TransportConfig config = new TransportConfig(); // 传输配置
StarlightClient starlightClient = new SingleStarlightClient("localhost", 8005, config);
starlightClient.init();
// 服务配置
ServiceConfig clientConfig = new ServiceConfig(); // 服务配置
clientConfig.setProtocol("brpc");
clientConfig.setServiceId("demo.UserService"); // 跨语言时指定服务端定义的serviceName
// 生成代理
JDKProxyFactory proxyFactory = new JDKProxyFactory();
UserProtoService userService = proxyFactory.getProxy(UserProtoService.class, clientConfig, starlightClient);
// 发起调用
UserPb.ExtInfoProto extInfoProto = UserPb.ExtInfoProto.newBuilder()
.setKey("brpckey")
.setValue("brpcvalue")
.build();
UserPb.UserProto user = UserPb.UserProto.newBuilder()
.setUserId(12L)
.setUserName("Brpc User")
.setAge(12)
.setAlive(true)
.setBalance(123.12d)
.setSex(UserPb.SexProto.FEMALE)
.setSalary(121.4f)
.setAddress(UserPb.AddressProto.newBuilder().setAddress("Beijing").build())
.addAllTags(Arrays.asList("BrpcTag1", "BrpcTags2"))
.addExtInfos(extInfoProto)
.build();
try {
UserPb.UserProto result = userService.Echo(user);
System.out.println("User result is " + result);
} catch (Exception e) {
System.out.println("Exception occur");
e.printStackTrace();
}
clusterClient.destroy();
System.exit(0);
}
}
添加管理员olivaw2077帮忙加群,备注starlight