The HeartbeatMonitor application incorporates .NET remoting to show an effective design pattern for using "heartbeats" to monitor components in a distributed application. This section uses the HeartbeatMonitor distributed application to demonstrate remote debugging across machines. An understanding of .NET remoting isn't essential to follow this debugging example, but if you're new to remoting, you might want to familiarize yourself with the remoting overview contained in the Visual Studio documentation.
The HeartbeatMonitor solution contains the four components that make up the HeartbeatMonitor application. Three of the components reside on one machine, and the final component is designed for installation on another machine in the same Windows domain. If you don't have access to a second machine, the application will run in its entirety on one machine, but this approach means that you won't be able to see remote debugging in action.
The HeartbeatMonitor application shows how you can use heartbeats to monitor components running within a distributed application. A heartbeat is a signal from one component to a monitoring component confirming that the first component is alive and functioning correctly.
HeartbeatMonitor 's first component consists of a single remotable class called Heartbeat that's shared between the other three components in the application. This class provides methods for components to generate and listen for heartbeats. It's built into its own class library called Heartbeat.dll .
The second component is a console application called HeartbeatClient . This component uses remoting and the Heartbeat class to emit regular heartbeats that can be heard by a local monitoring component that's listening on port 8080.
The third component is a monitoring application called LocalMonitor , also a console application. This component uses remoting to listen for any local component broadcasting heartbeats on port 8080. For reasons that I discuss later in the "Monitoring Distributed Applications" section, this component is designed to monitor only local components ”in other words, components that are running on the same machine as the monitor. In addition, LocalMonitor broadcasts its own heartbeats on port 8081, which was designed for monitoring by a listener on another machine.
The final component is yet another console application, this one called RemoteMonitor . This component is designed to run on a separate machine from the other components, and it uses remoting and the Heartbeat class to listen for a regular heartbeat from any remote component broadcasting on port 8081.
In essence, the LocalMonitor component sits between the HeartbeatClient and RemoteMonitor components, listening to local heartbeats from HeartbeatClient and generating remote heartbeats for RemoteMonitor .
I discuss the interesting architecture considerations behind this example application later in this chapter, when I look at effective and ineffective ways of monitoring a distributed application. For the moment, you just need to understand how the separate application components cooperate, so that you install the application and then follow the remote debugging walkthrough. Figure 15-6 shows the complete architecture of the HeartbeatMonitor application.
To verify that the example application is working correctly, load the HeartbeatMonitor solution into Visual Studio and right-click the solution in the Solution Explorer window. From the resulting context menu, go to the solution's Properties ’ Common Properties ’ Startup Project property page. Once there, ensure that the Multiple Startup Projects option is selected, and that the Start Action of all projects except Heartbeat is set to Start. Then go to the Project Dependencies property page and make sure that the project build dependencies are set correctly. The Heartbeat project is the base dependency and relies on none of the other projects. All of the other projects rely on just the Heartbeat project.
Now press Ctrl+Shift+B to build all of the projects. The Output window should show that all four projects were built without any errors or warnings. Once all of the projects in the solution have been built successfully, press Ctrl+F5 to start the solution with debugging disabled. If you're running a local firewall on your development PC, you may see some warning messages about allowing the application components to talk to each other through your firewall. The HeartbeatMonitor application uses remoting via two TCP channels on port 8080 and port 8081, so you may need to authorize the application to use these two ports. Alternatively, you can modify the source code and configuration files to specify different port numbers if your local firewall has any distinct preferences.
At this point, you should see three console windows opened respectively by the HeartbeatClient , LocalMonitor , and RemoteMonitor components. HeartbeatClient shows in its console window that it's started to emit a local heartbeat every 2 seconds. LocalMonitor shows that it's listening for local heartbeats and also generating a remote heartbeat every 10 seconds. Finally, RemoteMonitor shows that it's listening for remote heartbeats and prints each one that it receives. Figure 15-7 shows how the three console windows look while the HeartbeatMonitor application is running.
Once you've confirmed that the application is up and running properly on your local development machine, you can press the Enter key in each console window to stop the related application component. First stop HeartbeatClient , and LocalMonitor should show that it has stopped receiving heartbeats. Then stop LocalMonitor , and RemoteMonitor should show that it in turn has stopped receiving heartbeats (heartbeat messages are still displayed, but the time of the last heartbeat doesn't change). Finally, you can stop RemoteMonitor .
Now you've seen how the application works and how its components interact, in the next section you'll look at using remote debugging to debug this application after the RemoteMonitor component has been moved to another machine.
Before you do remote debugging of the HeartbeatMonitor application, you need to install the full remote debugging components on a PC other than your development machine. This process is described in the earlier section titled "Installing Full Remote Debugging." You should ensure that this second machine is in the same Windows domain as your development machine or at least on the same peer-to-peer network if that's what you have available. You should also log in using the same Windows user account on both machines before running and debugging the application, and that Windows user should be in the Debugger Users group on both machines. I discussed these restrictions earlier in the section titled "Remote Debugging Restrictions."
You also need to alter the line of code in the LocalMonitor component that controls the machine that the local monitor uses as the target for its remote heartbeats. To do this, change line 45 in LocalMonitor.vb to replace localhost with the IP address of your remote machine. For example, replace
You can perform either manual or automatic remote debugging of a VB .NET project. When you do automatic remote debugging, you tell Visual Studio that the project should be built, run, and debugged on a remote machine. When you do manual remote debugging, you build the project locally and then manually copy it to the remote machine. Once you've manually launched the component on the remote machine, you can manually attach the Visual Studio debugger to the remote process. I discuss these two methods of remote debugging in the next two sections.
The first step for automatic remote debugging is to set up a network share from the remote machine to your development machine, the development machine being the PC on which you're running Visual Studio. The network share should specify the remote folder where you want RemoteMonitor to be built and debugged. Once you've done this, go to the RemoteMonitor project's Configuration Properties ’ Build property page and enter the path of this network share into the Output path field, as shown in Figure 15-8.
As a final step for automatic remote debugging, go to the RemoteMonitor 's Configuration Properties ’ Debugging property page and select the option titled Use remote machine. In the associated text box, type the name of the remote machine.
Now when you start the solution by pressing F5, the RemoteMonitor project will be compiled and run on the remote machine rather than on your local machine. The debugger will automatically attach to the remote process, and you should be able to debug just as if you were debugging locally. You may find that remote debugging is somewhat slower than local debugging, but unfortunately I know of no way to speed up this process.
To check that you're debugging the remote project and not the local project, you can go to the Debug menu and select the Processes dialog window. In the Debugged Processes subwindow, you can verify that the machine name shown next to the RemoteMonitor process indicates the name of the remote machine. Before you close the Processes dialog window, there is one more interesting point. If you select any of the processes that are being debugged, you can see that the "When debugging is stopped" option is set to terminate the process. Unfortunately, this is the default setting that Visual Studio uses when attaching to a managed process, even though it's likely that this isn't what you normally want to do, especially in a distributed application. For instance, if you now press Enter in the HeartbeatClient console window to stop the client component, you'll find that Visual Studio stops the whole application, not just the client component. To prevent this behavior, you need to select each of the processes being debugged and set the "When debugging is stopped" option to detach from the process rather than terminate it. This manual procedure is a real pain when you're debugging multiple processes simultaneously .
Remote debugging can sometimes be a bit tricky to configure. For instance, if you have firewalls running on either of the two machines, you may need to change their configuration or even disable them. This is because remote debugging uses DCOM as its default transport protocol and firewalls usually don't allow DCOM by default. In fact, Microsoft claims not to support remote debugging through a firewall, although I've managed to do this without a problem.
In some cases, you might not want to build, run, and debug your remote component entirely within Visual Studio. For example, you might be debugging the RemoteMonitor component in a production environment without wanting to recompile it at all. For this, you need to do your remote debugging manually.
Before trying this, you should revert the RemoteMonitor component back to its original output path and local machine settings if you experimented with automatic remote debugging as described in the previous section. Then copy the contents of the RemoteMonitor\bin folder to a folder on the remote machine. This includes the RemoteMonitor and Heartbeat executables along with their associated debug symbol files and the RemoteMonitor.config file. Then change the HeartbeatMonitor solution to prevent it from launching the local RemoteMonitor project. On the solution's Properties ’ Common Properties ’ Startup Project property page, modify the Start Action of the RemoteMonitor project to None . Now launch the RemoteMonitor component from the remote machine by double-clicking the executable, and launch the rest of the application from Visual Studio by pressing F5.
You can now go to the Debug menu and select the Processes dialog window. In the Name text box, either type in the name of the remote machine or browse to it. You should then see a list of the processes running on the remote machine, including the RemoteMonitor process. Click that process and then click the Attach button. When the debugger displays a dialog window asking which type of debugging you wish to do, select just the Common Language Runtime option, as shown in Figure 15-9.
Now the Visual Studio debugger should attach to the remote process, and once again you're debugging just as if the RemoteMonitor component was running locally. The source stays on your local machine, but the executable and debug symbols are held remotely. One caveat with manual debugging in this fashion, whether done locally or remotely, is that you need to be really sure that the source code, executable, and debug symbols all match up exactly, otherwise you'll see strange things happen when the debugger tries to step through your source code. This is a good argument for always keeping matching debug symbols, source code, and executables together under rigorous source control, as Chapter 4 covered in more detail.