关键要点
在为 WSO2 API Manager 创建 API 网关时, 我们实际上是创建了与 API Manager 上发布的 API 相关的 Ballerina 服务.
请求过滤器可以访问传入的请求数据. 因此, 它可用于检查请求内容, 对内容进行验证, 复制并将请求推送到其他系统, 甚至修改原始请求.
默认情况下, 客户端应用程序要调用网关上的服务需要发送由受网关信任的 STS(安全令牌服务)签名的令牌(JWT). 调用请求需要符合 OAuth Bearer 配置.
你可以基于 Ballerina Streams 构建流式查询, 对从事件流上接收到的数据进行投影, 过滤, 窗口, 流连接和模式操作.
在微服务架构中, 用户需要生成 API 网关的容器运行时. 在 Ballerina 中, 我们可以在服务上使用相关的 Docker 和 Kubernetes 注解, 从而简化了这一过程.
现代 API 是一种具有良好定义且易于理解的网络功能, 可满足特定的业务需求. API 网关是架构模式中的一个层, 负责请求分配, 策略实施, 协议转换和分析, 让业务 API 专注于业务功能.
本文将介绍如何使用 Ballerina https://ballerina.io/ 为 WSO2 API Manager https://wso2.com/api-management/ 构建 API 网关. WSO2 API Manager 是一个开源的全生命周期 API 管理解决方案. 它具有设计和文档化 API 并使用各种策略发布 API 的能力. 还提供了一个开发者门户, 应用程序开发人员可以在上面发现和订阅 API. 它的安全组件为客户端应用程序提供了获取令牌的功能. 我们可以在基于 Ballerina 的 API 网关中应用 API 策略.
在 API Manager 中为 API 生成 Ballerina 服务
- import ballerina/http;
- import ballerina/log;
- // 目标端点
- endpoint http:Client targetEndpoint {
- url: "https://api.pizzastore.com/pizzashack/v1"
- };
- // 可以通过 / pizzashack/1.0.0 和 9090 端口访问这个服务
- @http:ServiceConfig {
- basePath: "/pizzashack/1.0.0"
- }
- service<http:Service> passthrough bind { port: 9090 } {
- @http:ResourceConfig {
- methods:["GET"],
- path: "/menu"
- }
- passthrough(endpoint caller, http:Request req) {
- // 把客户端请求转发到目标端点的 / menu 上
- var clientResponse = targetEndpoint->forward("/menu", req);
- // 检查端点的响应是否成功
- match clientResponse {
- http:Response res => {
- caller->respond(res) but { error e =>
- log:printError("Error sending response", err = e) };
- }
- error err => {
- http:Response res = new;
- res.statusCode = 500;
- res.setPayload(err.message);
- caller->respond(res) but { error e =>
- log:printError("Error sending response", err = e) };
- }
- }
- }
- }
- public type APIGatewayListener object {
- public {
- EndpointConfiguration config;
- http:Listener httpListener;
- }
- new () {
- httpListener = new;
- }
- public function init(EndpointConfiguration config);
- public function initEndpoint() returns (error);
- public function register(typedesc serviceType);
- public function start();
- public function getCallerActions() returns (http:Connection);
- public function stop();
- };
- public type AuthnFilter object {
- public function filterRequest (http:Request request, http:FilterContext
- context) returns http:FilterResult {
- import wso2/gateway;
- AuthnFilter authnFilter;
- OAuthzFilter authzFilter;
- RateLimitFilter rateLimitFilter;
- AnalyticsFilter analyticsFilter;
- ExtensionFilter extensionFilter;
- endpoint gateway:APIGatewayListener apiListener {
- port:9095,
- filters:[authnFilter, authzFilter, rateLimitFilter,
- analyticsFilter, extensionFilter]
- };
- // 可以通过 / pizzashack/1.0.0 和 9095 端口访问这个服务
- @http:ServiceConfig {
- basePath: "/pizzashack/1.0.0"
- }
- service<http:Service> passthrough bind apiListener {
- http:AuthProvider jwtAuthProvider = {
- scheme:"jwt",
- issuer:"ballerina",
- audience: "ballerina.io",
- certificateAlias: "ballerina",
- trustStore: {
- path: "${ballerina.home}/bre/security/ballerinaTruststore.p12",
- password: "ballerina"
- }
- };
- endpoint gateway:APIGatewayListener apiListener {
- port:9095,
- filters: ....,
- authProviders:[jwtAuthProvider]
- };
- // 可以通过 / pizzashack/1.0.0 和 9090 端口访问这个服务
- @http:ServiceConfig {
- basePath: "/pizzashack/1.0.0",
- authConfig: {
- authentication: { enabled: true }
- }
- }
- @http:ResourceConfig {
- methods:["GET"],
- path: "/menu",
- authConfig: {
- scopes: ["list_menu"]
- }
- }
- passthrough(endpoint caller, http:Request req) {
- {
- "sub": "ballerina",
- "iss": "ballerina",
- "exp": 2818415019,
- "iat": 1524575019,
- "jti": "f5aded50585c46f2b8ca233d0c2a3c9d",
- "aud": [
- "ballerina",
- "ballerina.org",
- "ballerina.io"
- ],
- "scope": "list_menu"
- }
- public stream<RequestStreamDTO> requestStream;
- public function publishNonThrottleEvent(RequestStreamDTO request) {
- requestStream.publish(request);
- }
- function initSubscriptionSilverPolicy() {
- stream<gateway:GlobalThrottleStreamDTO> resultStream;
- stream<gateway:EligibilityStreamDTO> eligibilityStream;
- forever {
- from gateway:requestStream
- select messageID, (subscriptionTier == "Silver") as isEligible,
- subscriptionKey as throttleKey
- => (gateway:EligibilityStreamDTO[] counts) {
- eligibilityStream.publish(counts);
- }
- from eligibilityStream
- throttler:timeBatch(60000, 0)
- where isEligible == true
- select throttleKey, count(messageID)>= 2000 as isThrottled,
- expiryTimeStamp
- group by throttleKey
- => (gateway:GlobalThrottleStreamDTO[] counts) {
- resultStream.publish(counts);
- }
- public function filterRequest(http:Request request, http:FilterContext context) returns http:FilterResult {
- http:FilterResult requestFilterResult;
- AnalyticsRequestStream requestStream = generateRequestEvent(request,
- context);
- EventDTO eventDto = generateEventFromRequest(requestStream);
- eventStream.publish(eventDto);
- requestFilterResult = { canProceed: true, statusCode: 200, message:
- "Analytics filter processed." };
- return requestFilterResult;
- }
- io:ByteChannel channel = io:openFile("api-usage-data.dat", io:APPEND);
- io:CharacterChannel charChannel = new(channel, "UTF-8");
- try {
- io:println("writing to events to a file");
- match charChannel.write(getEventData(eventDTO),0) {
- int numberOfCharsWritten => {
- io:println("No of characters written :" +
- numberOfCharsWritten);
- }
- error err => {
- throw err;
- }
- }
- } finally {
- match charChannel.close() {
- error sourceCloseError => {
- io:println("Error occured while closing the channel:" +
- sourceCloseError.message);
- }
- () => {
- io:println("Source channel closed successfully.");
- }
- }
- }
- @docker:Expose{}
- endpoint gateway:APIGatewayListener apiListener {
- port:9095,
- filters:[authnFilter, authzFilter, rateLimitFilter, analyticsFilter, extensionFilter]
- };
- @docker:Config {
- registry:"private.docker.gateway.com",
- name:"passthrough",
- tag:"v1.0"
- }
- service<http:Service> passthrough bind apiListener {
- DevOps
- Kubernetes
来源: http://www.infoq.com/cn/articles/ballerina-api-gateway