This presentation is shared by QCon上海2018|全球软件开发大会

You can download the PPT from 大会演讲PPT合集

Business Nature

  • Mobile is the first priority
  • Stable backend model, flexible frontend presentation
  • High Stability for business

Frontend Architecture

Below graph display the history of frontend architecture in Ant Financial.屏幕快照 2018-11-04 下午9.10.29

屏幕快照 2018-11-04 下午10.26.07 - translated

The frontend needs to be served in multiple channels. Not only traditional web UI, but also Little Program/H5 in various apps. Below graph introduces you how Ant Finance releases its change from source code to production.

屏幕快照 2018-11-05 下午10.07.13

How to process offline H5 page

屏幕快照 2018-11-05 下午10.10.21

It’s quite important these years to provide your service In-App. We have two ways channel (In China - Lawrence):
1. Little Program
Lightweight HTML app designs for running inside giant app like Wechat, Taobao, Alipay. Very similar to PWA. Little Program can access native function like using camera which Wechat will be a bridge for that.
2. H5
H5 stands for HTML 5 web page. When people compare it with Little Program, H5 represents the HTML 5 web app which will be loaded by WebView(embedded browser in native app).

屏幕快照 2018-11-05 下午10.20.22

Below image shows the differences of development for H5, Little Program and PC Web Page.

屏幕快照 2018-11-05 下午10.40.24

As the business grows, it’s a big challenge to develop the service. In a tradition frontend architecture model, UX API changes frequently, however service API is stable relatively. The question is, our API should be UI-oriented or should be generic.

And the answer is, Backend For Frontend(aka BFF).
屏幕快照 2018-11-05 下午10.46.57

In a tradition model, we basically have 3 layers: UI, business service, then infrastructure.
In BFF model, we still have 3 layers, the differences are:
In the 2nd layer, now we have BFFs which are designed for UI. BFF provides API that perfectly fit to UI. And we can have multiple BFF services designed for individual clients.
In the 3rd layer, we move Business Layer down. We treat Business Layer as some kind of infrastructure that provides generic business API.

屏幕快照 2018-11-05 下午11.44.36

In general, frontend and BFF development are completed by the same developer. From the development of employee point of view, there are some benefits:
1. Frontend developer has the opportunity to learn server side development skills
2. Expand the scope of knowledge
3. Improve communication skills
4. More reasonable division of labor

Following content is the open source solution Ant Finance creates. I would not repeat it, instead, I will paste the url links to GitHub or official home page.

Egg.js

GitHub - eggjs/egg: 🥚 Born to build better enterprise frameworks and apps with Node.js & Koa
egg - Born to build better enterprise frameworks and apps

GitHub - eggjs/awesome-egg: Awesome Egg.js Web Framework and Plugin.

Egg.js is a Node.js web development framework. It provides enterprise level features to speed up the development. It’s the core to BFF.

It’s worth to highlight that Egg.js is not only a web framework. It’s a solution to develop, build, run, monitor and maintain your Node.js application in production environment. For example, Egg.js embeds in a security mechanism to avoid security problem systematically, we all know that developer is not trustable: Security - Born to build better enterprise frameworks and apps.
And Egg.js provides a solution to run application in cluster:
Multi-Process Development Model Enhancement - Born to build better enterprise frameworks and apps.

Worth to have a look even if you’re not a fan to Egg.js itself.

sofa-rpc and sofa-rpc-node

GitHub - alipay/sofa-rpc: SOFARPC is a high-performance, high-extensibility, production-level Java RPC framework.

GitHub - alipay/sofa-rpc-node: SOFARPC Node is a high-performance, high-extensibility, production-level Nodejs RPC framework.

  • Supports multiple language
  • Automated serialization and deserialization
  • Transparent to developer to call JavaScript from Java or call Java from JavaScript
  • Support ProtoBuf and Hessian

High Quality Development

Development Steps

“屏幕快照 2018-11-06 下午9.19.13”的副本

Development Management

屏幕快照 2018-11-06 下午9.44.18

Change Management

  • Should be able to gated launch
  • Should be able to monitor
  • Should be able to roll back 屏幕快照 2018-11-06 下午9.49.18

Monitor

Backend Monitor

屏幕快照 2018-11-06 下午9.56.02

Frontend Monitor

Below content is shared by QCon上海2018|全球软件开发大会
You can download the PPT from: 大会演讲PPT合集

To be honest, you can find everything in AWS Lambda home page:
AWS Lambda云计算服务介绍_如何使用AWS Lambda-AWS云服务

What is Serverless? Serverless let you run code without thinking about servers. You don’t need to manage your physical server anymore. You can easily have a good scalability, high availability and fault tolerance. And pay only for the compute time you consume. And this features let you.

Running code without thinking about servers

If you use AWS Lambda service, you don’t need to maintains your physical machine anymore. What you need to do, is to upload to code block(which we also call it lambda) to AWS, and AWS will run your code when a request comes in.

Scalability

A lambda is a piece of code than runs independently. So there is no double that you can scale up your service by running hundreds of, thousands of or billions of lambdas simultaneously, to serve your successful business. Scaling up/down is an automatic process therefore you only need focus on your own business.

High Availability and Fault Tolerance

Same reason as above, serverless gives your an architecture with high availability and fault tolerance.

Paying only for the compute time you consume

The money you pay depends on the duration your lambda runs. It significantly cuts down your cost. And you can manage your cost much clearly. You can understand which function consumes most of your money. And you can have the visibility to your code improvement. Because if you reduce your request processing time from 10s to 5s, you only need to pay half of the bill.

How a Lambda Run

Running a lambda in AWS is trivial. Write the code in your computer, upload to AWS, set a HTTP url, that’s it.

Event Driven

A lambda is triggered by every single HTTP request. This is a so-called event driven architecture. It also means the integration with other services is easy.

Step Function

Things can become complicated if your lambda workflow grows. Try to image that your service is constructed by 10 or 15 lambdas and there are 4 or 5 if-else logic. This would not be an easy job to develop and maintain.

This is the reason why we have AWS Step Function.
AWS Step Functions可视化工作流服务_分布式应用调节-AWS云服务

The weaknesses

First thing need to highlight here, is that severless is not prefect solution and you can’t really say serverless is the future. So above words introduces what’s good for serverless and now it’s time to tell you what serverless can’t do.

Firstly, a time-sensitive service can’t be served by serverless(as they’re driven by HTTP API).

Secondly, cold start issue. When the first time a lambda is called, AWS need to download your scripts, build the environment you need, then run it. So at this moment you have to warn up your service by ourselves if this is not acceptable for you or your business.

Thirdly, your lambda can’t run for long. It’s not a limitation and a technical issue for AWS Lambda Service. It’s about the cost performance. If your program need to run 24 hours, holding an EC2 instance costs you less.

And the last, we do need more a more third party services adapt to serverless which we don’t have right now.


Below content comes from QCon上海2018|全球软件开发大会
You can download slideshow from 大会演讲PPT合集

In this presentation, Qiao introduced DevOps to us from a historical point of view.

In the 1970s, we develop our software in a so-called waterfall model.
In the 1990s, we have Agile.
When it comes to 2010s, we made Continue Delivery.
We drive our development in a model that is from per project to per requirement, we build, test, and deliver out software from per month to per day, per hour or even per minute

Qiao said, we’re in a loop that he call it software delivery cycle. We make a plan, we code and build our software, we run the software, we monitor our software, and then we start again with a new plan.

Plan -> Build -> Run -> Monitoring -> Plan

In this cycle, Agile help us to make plan become a build quickly(from Plan to Build). And DevOps helps on Build to Run.

If we think about it from a people point of view, Agile creates an efficient communication environment for project manager, developer, and test engineer. Continue Integration(CI) quickly passes the software package to test engineer. And what DevOps solves, is to make developer tester, and operation and maintenance engineer cooperate better.

Qiao highlighted a sentence: “There is not an authoritative definition for DevOps”. By this way, he thinks that both Agile and DevOps are the same thing. They’re looking for a series of ways and practices to improve the cooperate quality and efficiency for different roles in software development, in order to speed up the delivery.

People and companies use tons of tools to make DevOps easier.
3341B206-FCAA-4C0B-8D4E-8750651FE599
(Above image comes from: Periodic Table of DevOps Tools)

And companies also hire DevOps engineer, to try to apply DevOps in the development. However, sometimes and somehow tools and DevOps engineer may not able to help and even worst they will have a negative effect.

People expect to deliver changes faster and faster, from 1 month to 1 day. Generally, people start DevOps quickly and easily. People start with process automation(I don’t know what process is, from Lawrence), and then they will make automation test. People will gain a huge benefit from this 2 steps for sure. And people will try to create more and more automation tests which will not help to step onto next phase for DevOps. Qiao called it Overmuch Automation Test.

So, how can we have a better DevOps?
Qiao raises 2 key points:
1. Understand measurement correctly
2. Have 2nd evolution for DevOps

Understand Measurement

Measurement has to be visible, timely and actable. And most of the measurement is not quite actable. You can’t really take action immediately and directly when you check a measurement. And in order to turn measurement into action, you need lots of analysis and more and more other measurements. Things become complicated and you may not able to take a correct action from the result of the analysis.

And you will have to know:
Measuring things has its cost.
Measurement can’t tell everything to you.
Dev lead should find a way to manage the thing that can’t be measured.

Have 2nd evolution on DevOps

In the first evolution, people apply to DevOps without breaking the development model.
And in second evolution, we need to break the old world and create a new one(if need).

One example is, to change the user from tester to developer for automation test.
Before the change, test cases are created and run by tester. For the seek of that dev will willing to use it, we need to make the test cases to become:
* Running fast
* Easy to use
* Result should be reliable
* Test cases are up to date
It does take time and money to achieve above 4 goals.

(To explain more, above example is not only an IT problem. It’s already a common sense that Dev takes the responsibility to test, create automation test and maintain automation test in Evolve. But in other companies and if you’re in a role to push DevOps, you need to get human resource, money , and priority from your boss. And you will break the daily workflow for your workmates which leads to antagonistic sentiments. Department structrue need to be changed as well sometimes and it’s a problem can be solved from bottom to top. from Lawrence)

And Qiao introduces a concept - Small Business Unit(SBU) - to us.
SBU is to have less communicate cost and more alignment. A SBU contains business people, project manager, developer, test engineer, and operate and maintenance engineer. And that requires you not only pay attention to your development infrastructure, but also to your software architecture, company structure, culture, and incentive system.

In the end, Qiao recommends his book Continue Delivery: 2.0.

posted in JavaScript 

Today my workmate hit an issue that v-if didn't work for him.
It turned out that Vue.js can't watch the property that you do not define at the beginning.

Root Cause

It's not only relative to v-if. It impact all functions that used two-way data binding.

On below demo, we have two section that both of them hope to change content by button clicking. Section 1 works good but section 2 didn't.

It's because section2 object(in js) doesn't define content property.
So after Vue runs up, the new property that you add afterward can't be watched by Vue. And then you will fine your change will not reflect to web page even if you change the value of the property.

In this demo, I tried to change the content of section 2 but it didn't work. In my workmate's case, it change the value of a property but v-if was not triggered.

Live Demo: https://jsfiddle.net/Lawrence/7eq3y5uv/

Look Deeper

When Vue.js starts, it will convert all data into observable. The key is how Vue.js do that here.

Vue.js will bind Watcher on each properties of an object by defining set method using Object.defineProperty. Therefore Vue.js can get updated if you change the value.

Apparently, if you don't define an property for an object at the beginning, Vue.js can't watch it.

这是一系列来自阿里的 ElasticSearch 的原理剖析文章。文章质量优秀,干货十足。

一是 ES 是非常流行的分布式搜索引擎,不管是日常运维还是生产环境都非常实用。
二是 ES 也是一个典型的分布式系统。分布式系统避不开的集群、服务发现、Master 选举等话题在此系列文章中都有涉及。如果想了解如何开发一个高可用,可伸缩的分布式系统,ES 也是一个很好的例子。

Elasticsearch分布式一致性原理剖析(一)-节点篇

Elasticsearch分布式一致性原理剖析(二)-Meta篇

Elasticsearch分布式一致性原理剖析(三)-Data篇

posted in Java 

It's an important feature that JVM provided and I'm so surprised that I used it at the first time today.

How to enable remote debugging

To enable it, you will just need to add below JVM arguments in command line:

-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

Then you can use your debugger to attach to remote JVM.

suspend is quite useful argument here. If suspend is y, JVM will suspend before main class is loaded until debugger attach to it. n otherwise.

So set suspend to y if you need to debug application initialization phase.

Check Official Document for detailed introduction

Remote Debugging in IntelliJ IDEA

I believe all IDEs have that function to debug Java application remotely. I use IntelliJ here for demonstration.

Remote Debug Configuration in IntelliJ
Add a new configuration under Remote category, you will see IntelliJ provides command line arguments for you. That's exactly what I show in previous section.

Change the host to your server's url, and launch your application with remote debug arguments, then you can debug as what you did on debugging local application.

DO NOT REMOTE DEBUG YOUR PRODUCTION APPLICATION

I confused for so long that what Cherry-Pick is. And today I got some times to learn it.

What Cherry-Pick can do

Cherry-Pick can pick some commits from other branch, merge into your current working branch.

Use Cases

  • If you're working on a dev branch. You have not yet complete your task but there are change committed. And now you need to merge part of you work into master for some reason but you don't want to merge the dev branch into master. And now you can cherry pick the commits that just you need, and merge them into master.

  • If you merge your commit into wrong branch. And now you need to merge you change into master. You can cherry pick you change and merge into master branch. Then revert your change into that wrong branch.

Many years ago I have heard that docker is an amazing stuff to manage the build, deployment and service life cycle. But at that time, there were also some bad news on security that prevented me from trying it.

And today I finally found a day to play with it. The result is, Docker is a amazing thing to manage my service. It exactly fix my problem that this years I'm trying to improve and contributing to my business.

Service Isolation

Docker will create individual VM to serve each services. Most of OS resource are isolated so that dev doesn't need to care about the system resource(at least less care about it). Also we can keep clean codebase without system specified logic.

In my company we run services in several hosts shared to different teams. It's not a frequent case but also annoying that different services will try to use same port, will try to user different JDK version on the path, will need to change same environment variable.

Some service change those share resource dynamically and leads to random failures/issues that hard to locate.

In this scenario, Docker may be one of solutions.

Unified Stander to Deploy Services

DevOps requires less manual work on our build, test, release process. All stuffs should be able to automatically proceed. It absolutely is a good idea. When we serve our system in a large scale and cooperate with so much teams, more manual work we need, much risk we introduce. No one want to stick in fire-fight at mid night. And Docker provide a unified stander to manage service's resource and environment. That means, we are easily to write automatic process for build, for test and for deployment.

The company I work on has its own solution to manage resource and do things automatically. We have huge set of scripts and softwares to setup dev environment, setup build agents for TeamCity and Jenkins, to deploy application, to verify or monitor the function we provide.
They are awesome, they have provided huge value to business. But it's customized. New engineers are hard to understand the ecosystem.

I think Docker is good way to reduce the learning cost because of aligned style to manage resource

Scalability

It's the most well-known feature for Docker, no need more words to admire it.

posted in Java 

Today I'm trying to make an endpoint accessible in different paths. Such as I can go to http://localhost/ and http://localhost/hello for a same hello message.

I do got some solutions.

I'm using Jersey over Jetty. But whether or not you're using Jetty servlet or writing Jersey Resource, I think you will get some ideas.

Ps: I copy the code from my GitLab Snippets and will keep it update. So If you find something wrong, pls comment in GitLab

Using the regular expression in Jersey Resource

https://gitlab.com/snippets/1676612

package me.imlc.example.jetty.multiplehandler;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import me.imlc.example.jetty.multplehandler.HelloResource;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Objects;

import static org.junit.Assert.assertEquals;

public class RegexpResourceTest {

    private static Server server;
    private static CloseableHttpClient client = HttpClients.createDefault();

    @BeforeClass
    public static void beforeAll() throws Exception {
        ResourceConfig rc = new ResourceConfig(RegexpResource.class);

        ServletHolder holder = new ServletHolder(new ServletContainer(rc));

        server = new Server(8080);

        ServletContextHandler contextHandler = new ServletContextHandler(server, "/*");
        contextHandler.addServlet(holder, "/*");

        server.start();
    }


    @Test
    public void canAccessByRootPath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @Test
    public void canAccessByServicePath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/service/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @AfterClass
    public static void afterAll() throws Exception {
        if(Objects.nonNull(server)) server.stop();
        if(Objects.nonNull(client)) client.close();
    }

    @Path("/{a:(.*)|service}")
    public static class RegexpResource {
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() {
            return "Hello, Lynn.";
        }
    }
}

Specify multiple paths for one servlet

https://gitlab.com/snippets/1676613

package me.imlc.example.jetty.multiplehandler;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Objects;

import static org.junit.Assert.assertEquals;

public class MultipleServletTest {

    private static Server server;
    private static CloseableHttpClient client = HttpClients.createDefault();

    @BeforeClass
    public static void beforeAll() throws Exception {
        ResourceConfig rc = new ResourceConfig(RegexpResourceTest.RegexpResource.class);

        ServletHolder holder = new ServletHolder(new ServletContainer(rc));

        server = new Server(8080);

        ServletContextHandler contextHandler = new ServletContextHandler(server, "/*");
        contextHandler.addServlet(holder, "/*");
        contextHandler.addServlet(holder, "/service/*");

        server.start();
    }

    @Test
    public void canAccessByRootPath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @Test
    public void canAccessByServicePath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/service/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @AfterClass
    public static void afterAll() throws Exception {
        if(Objects.nonNull(server)) server.stop();
        if(Objects.nonNull(client)) client.close();
    }

    @Path("/hello")
    public static class MultipleServletResource {
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() {
            return "Hello, Lynn.";
        }
    }
}

Serve one servlet in different context handler

https://gitlab.com/snippets/1676615

package me.imlc.example.jetty.multiplehandler;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Objects;

import org.eclipse.jetty.server.handler.ContextHandlerCollection;

import static org.junit.Assert.assertEquals;

public class MultipleContextHandlerTest {
    private static Server server;
    private static CloseableHttpClient client = HttpClients.createDefault();

    @BeforeClass
    public static void beforeAll() throws Exception {
        ResourceConfig rc = new ResourceConfig(MultipleContextHandlerResource.class);

        ServletHolder holder = new ServletHolder(new ServletContainer(rc));

        server = new Server(8080);

        ServletContextHandler rootContext = new ServletContextHandler(server, "/*");
        rootContext.addServlet(holder, "/*");

        ServletContextHandler serviceContext = new ServletContextHandler(server, "/service/*");
        serviceContext.addServlet(holder, "/*");

        ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection();
        contextHandlerCollection.setHandlers(new Handler[]{
                rootContext, serviceContext
        });

        server.setHandler(contextHandlerCollection);

        server.start();
    }

    @Test
    public void canAccessByRootPath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @Test
    public void canAccessByServicePath() throws Exception {
        HttpGet get = new HttpGet("http://localhost:8080/service/hello");
        String content = EntityUtils.toString(client.execute(get).getEntity());
        assertEquals(content, "Hello, Lynn.");
    }

    @AfterClass
    public static void afterAll() throws Exception {
        if(Objects.nonNull(server)) server.stop();
        if(Objects.nonNull(client)) client.close();
    }

    @Path("/hello")
    public static class MultipleContextHandlerResource {
        @GET
        @Produces(MediaType.TEXT_PLAIN)
        public String hello() {
            return "Hello, Lynn.";
        }
    }
}